From fdc8c3509b5ac5bc170c54d72b2c2bafb58409f2 Mon Sep 17 00:00:00 2001 From: Markus Roberts Date: Mon, 28 Jun 2010 17:10:20 -0700 Subject: [#3994-part 3] rename spec tests from *_spec_spec to *_spec.rb Part 2 re-did the change on the spec files, which it shouldn't have. --- spec/unit/agent/locker_spec.rb | 100 ++ spec/unit/agent/locker_spec_spec.rb | 100 -- spec/unit/agent_spec.rb | 281 +++++ spec/unit/agent_spec_spec.rb | 281 ----- spec/unit/application/agent_spec.rb | 600 +++++++++++ spec/unit/application/agent_spec_spec.rb | 600 ----------- spec/unit/application/apply_spec.rb | 387 +++++++ spec/unit/application/apply_spec_spec.rb | 387 ------- spec/unit/application/cert_spec.rb | 172 +++ spec/unit/application/cert_spec_spec.rb | 172 --- spec/unit/application/describe_spec.rb | 84 ++ spec/unit/application/describe_spec_spec.rb | 84 -- spec/unit/application/doc_spec.rb | 368 +++++++ spec/unit/application/doc_spec_spec.rb | 368 ------- spec/unit/application/filebucket_spec.rb | 223 ++++ spec/unit/application/filebucket_spec_spec.rb | 223 ---- spec/unit/application/kick_spec.rb | 306 ++++++ spec/unit/application/kick_spec_spec.rb | 306 ------ spec/unit/application/master_spec.rb | 447 ++++++++ spec/unit/application/master_spec_spec.rb | 447 -------- spec/unit/application/queue_spec.rb | 186 ++++ spec/unit/application/queue_spec_spec.rb | 186 ---- spec/unit/application/resource_spec.rb | 233 ++++ spec/unit/application/resource_spec_spec.rb | 233 ---- spec/unit/application_spec.rb | 519 +++++++++ spec/unit/application_spec_spec.rb | 519 --------- spec/unit/configurer/downloader_spec.rb | 188 ++++ spec/unit/configurer/downloader_spec_spec.rb | 188 ---- spec/unit/configurer/fact_handler_spec.rb | 164 +++ spec/unit/configurer/fact_handler_spec_spec.rb | 164 --- spec/unit/configurer/plugin_handler_spec.rb | 112 ++ spec/unit/configurer/plugin_handler_spec_spec.rb | 112 -- spec/unit/configurer_spec.rb | 494 +++++++++ spec/unit/configurer_spec_spec.rb | 494 --------- spec/unit/daemon_spec.rb | 306 ++++++ spec/unit/daemon_spec_spec.rb | 306 ------ spec/unit/dsl/resource_api_spec.rb | 181 ++++ spec/unit/dsl/resource_api_spec_spec.rb | 181 ---- spec/unit/dsl/resource_type_api_spec.rb | 46 + spec/unit/dsl/resource_type_api_spec_spec.rb | 46 - spec/unit/file_bucket/dipper_spec.rb | 110 ++ spec/unit/file_bucket/dipper_spec_spec.rb | 110 -- spec/unit/file_bucket/file_spec.rb | 230 ++++ spec/unit/file_bucket/file_spec_spec.rb | 230 ---- spec/unit/file_collection/lookup_spec.rb | 46 + spec/unit/file_collection/lookup_spec_spec.rb | 46 - spec/unit/file_collection_spec.rb | 53 + spec/unit/file_collection_spec_spec.rb | 53 - spec/unit/file_serving/base_spec.rb | 132 +++ spec/unit/file_serving/base_spec_spec.rb | 132 --- .../unit/file_serving/configuration/parser_spec.rb | 181 ++++ .../file_serving/configuration/parser_spec_spec.rb | 181 ---- spec/unit/file_serving/configuration_spec.rb | 249 +++++ spec/unit/file_serving/configuration_spec_spec.rb | 249 ----- spec/unit/file_serving/content_spec.rb | 118 +++ spec/unit/file_serving/content_spec_spec.rb | 118 --- spec/unit/file_serving/fileset_spec.rb | 347 ++++++ spec/unit/file_serving/fileset_spec_spec.rb | 347 ------ spec/unit/file_serving/indirection_hooks_spec.rb | 63 ++ .../file_serving/indirection_hooks_spec_spec.rb | 63 -- spec/unit/file_serving/metadata_spec.rb | 286 +++++ spec/unit/file_serving/metadata_spec_spec.rb | 286 ----- spec/unit/file_serving/mount/file_spec.rb | 196 ++++ spec/unit/file_serving/mount/file_spec_spec.rb | 196 ---- spec/unit/file_serving/mount/modules_spec.rb | 63 ++ spec/unit/file_serving/mount/modules_spec_spec.rb | 63 -- spec/unit/file_serving/mount/plugins_spec.rb | 61 ++ spec/unit/file_serving/mount/plugins_spec_spec.rb | 61 -- spec/unit/file_serving/mount_spec.rb | 32 + spec/unit/file_serving/mount_spec_spec.rb | 32 - spec/unit/file_serving/terminus_helper_spec.rb | 98 ++ .../unit/file_serving/terminus_helper_spec_spec.rb | 98 -- spec/unit/indirector/active_record_spec.rb | 76 ++ spec/unit/indirector/active_record_spec_spec.rb | 76 -- spec/unit/indirector/catalog/active_record_spec.rb | 141 +++ .../indirector/catalog/active_record_spec_spec.rb | 141 --- spec/unit/indirector/catalog/compiler_spec.rb | 265 +++++ spec/unit/indirector/catalog/compiler_spec_spec.rb | 265 ----- spec/unit/indirector/catalog/queue_spec.rb | 20 + spec/unit/indirector/catalog/queue_spec_spec.rb | 20 - spec/unit/indirector/catalog/rest_spec.rb | 11 + spec/unit/indirector/catalog/rest_spec_spec.rb | 11 - spec/unit/indirector/catalog/yaml_spec.rb | 25 + spec/unit/indirector/catalog/yaml_spec_spec.rb | 25 - spec/unit/indirector/certificate/ca_spec.rb | 28 + spec/unit/indirector/certificate/ca_spec_spec.rb | 28 - spec/unit/indirector/certificate/file_spec.rb | 28 + spec/unit/indirector/certificate/file_spec_spec.rb | 28 - spec/unit/indirector/certificate/rest_spec.rb | 58 + spec/unit/indirector/certificate/rest_spec_spec.rb | 58 - .../unit/indirector/certificate_request/ca_spec.rb | 19 + .../indirector/certificate_request/ca_spec_spec.rb | 19 - .../indirector/certificate_request/file_spec.rb | 19 + .../certificate_request/file_spec_spec.rb | 19 - .../indirector/certificate_request/rest_spec.rb | 23 + .../certificate_request/rest_spec_spec.rb | 23 - .../certificate_revocation_list/ca_spec.rb | 21 + .../certificate_revocation_list/ca_spec_spec.rb | 21 - .../certificate_revocation_list/file_spec.rb | 20 + .../certificate_revocation_list/file_spec_spec.rb | 20 - .../certificate_revocation_list/rest_spec.rb | 23 + .../certificate_revocation_list/rest_spec_spec.rb | 23 - spec/unit/indirector/code_spec.rb | 33 + spec/unit/indirector/code_spec_spec.rb | 33 - spec/unit/indirector/direct_file_server_spec.rb | 84 ++ .../indirector/direct_file_server_spec_spec.rb | 84 -- spec/unit/indirector/envelope_spec.rb | 47 + spec/unit/indirector/envelope_spec_spec.rb | 47 - spec/unit/indirector/exec_spec.rb | 56 + spec/unit/indirector/exec_spec_spec.rb | 56 - spec/unit/indirector/facts/active_record_spec.rb | 104 ++ .../indirector/facts/active_record_spec_spec.rb | 104 -- spec/unit/indirector/facts/couch_spec.rb | 98 ++ spec/unit/indirector/facts/couch_spec_spec.rb | 98 -- spec/unit/indirector/facts/facter_spec.rb | 142 +++ spec/unit/indirector/facts/facter_spec_spec.rb | 142 --- spec/unit/indirector/facts/rest_spec.rb | 11 + spec/unit/indirector/facts/rest_spec_spec.rb | 11 - spec/unit/indirector/facts/yaml_spec.rb | 26 + spec/unit/indirector/facts/yaml_spec_spec.rb | 26 - spec/unit/indirector/file_bucket_file/file_spec.rb | 290 +++++ .../indirector/file_bucket_file/file_spec_spec.rb | 290 ----- spec/unit/indirector/file_bucket_file/rest_spec.rb | 11 + .../indirector/file_bucket_file/rest_spec_spec.rb | 11 - .../indirector/file_content/file_server_spec.rb | 18 + .../file_content/file_server_spec_spec.rb | 18 - spec/unit/indirector/file_content/file_spec.rb | 18 + .../unit/indirector/file_content/file_spec_spec.rb | 18 - spec/unit/indirector/file_content/rest_spec.rb | 11 + .../unit/indirector/file_content/rest_spec_spec.rb | 11 - .../indirector/file_metadata/file_server_spec.rb | 18 + .../file_metadata/file_server_spec_spec.rb | 18 - spec/unit/indirector/file_metadata/file_spec.rb | 52 + .../indirector/file_metadata/file_spec_spec.rb | 52 - spec/unit/indirector/file_metadata/rest_spec.rb | 9 + .../indirector/file_metadata/rest_spec_spec.rb | 9 - spec/unit/indirector/file_server_spec.rb | 267 +++++ spec/unit/indirector/file_server_spec_spec.rb | 267 ----- spec/unit/indirector/file_spec.rb | 181 ++++ spec/unit/indirector/file_spec_spec.rb | 181 ---- spec/unit/indirector/indirection_spec.rb | 795 ++++++++++++++ spec/unit/indirector/indirection_spec_spec.rb | 795 -------------- spec/unit/indirector/key/ca_spec.rb | 28 + spec/unit/indirector/key/ca_spec_spec.rb | 28 - spec/unit/indirector/key/file_spec.rb | 104 ++ spec/unit/indirector/key/file_spec_spec.rb | 104 -- spec/unit/indirector/ldap_spec.rb | 143 +++ spec/unit/indirector/ldap_spec_spec.rb | 143 --- spec/unit/indirector/memory_spec.rb | 29 + spec/unit/indirector/memory_spec_spec.rb | 29 - spec/unit/indirector/node/active_record_spec.rb | 34 + .../indirector/node/active_record_spec_spec.rb | 34 - spec/unit/indirector/node/exec_spec.rb | 69 ++ spec/unit/indirector/node/exec_spec_spec.rb | 69 -- spec/unit/indirector/node/ldap_spec.rb | 456 ++++++++ spec/unit/indirector/node/ldap_spec_spec.rb | 456 -------- spec/unit/indirector/node/memory_spec.rb | 19 + spec/unit/indirector/node/memory_spec_spec.rb | 19 - spec/unit/indirector/node/plain_spec.rb | 19 + spec/unit/indirector/node/plain_spec_spec.rb | 19 - spec/unit/indirector/node/rest_spec.rb | 13 + spec/unit/indirector/node/rest_spec_spec.rb | 13 - spec/unit/indirector/node/yaml_spec.rb | 25 + spec/unit/indirector/node/yaml_spec_spec.rb | 25 - spec/unit/indirector/plain_spec.rb | 29 + spec/unit/indirector/plain_spec_spec.rb | 29 - spec/unit/indirector/queue_spec.rb | 123 +++ spec/unit/indirector/queue_spec_spec.rb | 123 --- spec/unit/indirector/report/processor_spec.rb | 81 ++ spec/unit/indirector/report/processor_spec_spec.rb | 81 -- spec/unit/indirector/report/rest_spec.rb | 28 + spec/unit/indirector/report/rest_spec_spec.rb | 28 - spec/unit/indirector/request_spec.rb | 304 ++++++ spec/unit/indirector/request_spec_spec.rb | 304 ------ spec/unit/indirector/resource/ral_spec.rb | 129 +++ spec/unit/indirector/resource/ral_spec_spec.rb | 129 --- spec/unit/indirector/resource/rest_spec.rb | 11 + spec/unit/indirector/resource/rest_spec_spec.rb | 11 - spec/unit/indirector/resource_type/parser_spec.rb | 75 ++ .../indirector/resource_type/parser_spec_spec.rb | 75 -- spec/unit/indirector/resource_type/rest_spec.rb | 15 + .../indirector/resource_type/rest_spec_spec.rb | 15 - spec/unit/indirector/rest_spec.rb | 455 ++++++++ spec/unit/indirector/rest_spec_spec.rb | 455 -------- spec/unit/indirector/run/local_spec.rb | 20 + spec/unit/indirector/run/local_spec_spec.rb | 20 - spec/unit/indirector/run/rest_spec.rb | 11 + spec/unit/indirector/run/rest_spec_spec.rb | 11 - spec/unit/indirector/ssl_file_spec.rb | 281 +++++ spec/unit/indirector/ssl_file_spec_spec.rb | 281 ----- spec/unit/indirector/status/rest_spec.rb | 11 + spec/unit/indirector/status/rest_spec_spec.rb | 11 - spec/unit/indirector/terminus_spec.rb | 245 +++++ spec/unit/indirector/terminus_spec_spec.rb | 245 ----- spec/unit/indirector/yaml_spec.rb | 146 +++ spec/unit/indirector/yaml_spec_spec.rb | 146 --- spec/unit/indirector_spec.rb | 157 +++ spec/unit/indirector_spec_spec.rb | 157 --- spec/unit/module_spec.rb | 546 ++++++++++ spec/unit/module_spec_spec.rb | 546 ---------- spec/unit/network/authconfig_spec.rb | 292 +++++ spec/unit/network/authconfig_spec_spec.rb | 292 ----- spec/unit/network/authstore_spec.rb | 370 +++++++ spec/unit/network/authstore_spec_spec.rb | 370 ------- spec/unit/network/client_spec.rb | 45 + spec/unit/network/client_spec_spec.rb | 45 - spec/unit/network/format_handler_spec.rb | 336 ++++++ spec/unit/network/format_handler_spec_spec.rb | 336 ------ spec/unit/network/format_spec.rb | 198 ++++ spec/unit/network/format_spec_spec.rb | 198 ---- spec/unit/network/formats_spec.rb | 337 ++++++ spec/unit/network/formats_spec_spec.rb | 337 ------ spec/unit/network/handler/fileserver_spec.rb | 176 ++++ spec/unit/network/handler/fileserver_spec_spec.rb | 176 ---- spec/unit/network/http/api/v1_spec.rb | 122 +++ spec/unit/network/http/api/v1_spec_spec.rb | 122 --- spec/unit/network/http/compression_spec.rb | 199 ++++ spec/unit/network/http/compression_spec_spec.rb | 199 ---- spec/unit/network/http/handler_spec.rb | 455 ++++++++ spec/unit/network/http/handler_spec_spec.rb | 455 -------- spec/unit/network/http/mongrel/rest_spec.rb | 249 +++++ spec/unit/network/http/mongrel/rest_spec_spec.rb | 249 ----- spec/unit/network/http/mongrel/xmlrpc_spec.rb | 0 spec/unit/network/http/mongrel/xmlrpc_spec_spec.rb | 0 spec/unit/network/http/mongrel_spec.rb | 131 +++ spec/unit/network/http/mongrel_spec_spec.rb | 131 --- spec/unit/network/http/rack/rest_spec.rb | 249 +++++ spec/unit/network/http/rack/rest_spec_spec.rb | 249 ----- spec/unit/network/http/rack/xmlrpc_spec.rb | 157 +++ spec/unit/network/http/rack/xmlrpc_spec_spec.rb | 157 --- spec/unit/network/http/rack_spec.rb | 102 ++ spec/unit/network/http/rack_spec_spec.rb | 102 -- spec/unit/network/http/webrick/rest_spec.rb | 180 ++++ spec/unit/network/http/webrick/rest_spec_spec.rb | 180 ---- spec/unit/network/http/webrick/xmlrpc_spec.rb | 0 spec/unit/network/http/webrick/xmlrpc_spec_spec.rb | 0 spec/unit/network/http/webrick_spec.rb | 339 ++++++ spec/unit/network/http/webrick_spec_spec.rb | 339 ------ spec/unit/network/http_pool_spec.rb | 206 ++++ spec/unit/network/http_pool_spec_spec.rb | 206 ---- spec/unit/network/http_spec.rb | 35 + spec/unit/network/http_spec_spec.rb | 35 - spec/unit/network/rest_authconfig_spec.rb | 148 +++ spec/unit/network/rest_authconfig_spec_spec.rb | 148 --- spec/unit/network/rest_authorization_spec.rb | 43 + spec/unit/network/rest_authorization_spec_spec.rb | 43 - spec/unit/network/rights_spec.rb | 519 +++++++++ spec/unit/network/rights_spec_spec.rb | 519 --------- spec/unit/network/server_spec.rb | 534 ++++++++++ spec/unit/network/server_spec_spec.rb | 534 ---------- spec/unit/network/xmlrpc/client_spec.rb | 171 +++ spec/unit/network/xmlrpc/client_spec_spec.rb | 171 --- spec/unit/node/environment_spec.rb | 248 +++++ spec/unit/node/environment_spec_spec.rb | 248 ----- spec/unit/node/facts_spec.rb | 102 ++ spec/unit/node/facts_spec_spec.rb | 102 -- spec/unit/node_spec.rb | 210 ++++ spec/unit/node_spec_spec.rb | 210 ---- spec/unit/other/selinux_spec.rb | 85 ++ spec/unit/other/selinux_spec_spec.rb | 85 -- spec/unit/other/transbucket_spec.rb | 188 ++++ spec/unit/other/transbucket_spec_spec.rb | 188 ---- spec/unit/other/transobject_spec.rb | 112 ++ spec/unit/other/transobject_spec_spec.rb | 112 -- spec/unit/parameter/value_collection_spec.rb | 167 +++ spec/unit/parameter/value_collection_spec_spec.rb | 167 --- spec/unit/parameter/value_spec.rb | 88 ++ spec/unit/parameter/value_spec_spec.rb | 88 -- spec/unit/parameter_spec.rb | 172 +++ spec/unit/parameter_spec_spec.rb | 172 --- spec/unit/parser/ast/arithmetic_operator_spec.rb | 73 ++ .../parser/ast/arithmetic_operator_spec_spec.rb | 73 -- spec/unit/parser/ast/astarray_spec.rb | 72 ++ spec/unit/parser/ast/astarray_spec_spec.rb | 72 -- spec/unit/parser/ast/asthash_spec.rb | 98 ++ spec/unit/parser/ast/asthash_spec_spec.rb | 98 -- spec/unit/parser/ast/boolean_operator_spec.rb | 53 + spec/unit/parser/ast/boolean_operator_spec_spec.rb | 53 - spec/unit/parser/ast/casestatement_spec.rb | 159 +++ spec/unit/parser/ast/casestatement_spec_spec.rb | 159 --- spec/unit/parser/ast/collection_spec.rb | 63 ++ spec/unit/parser/ast/collection_spec_spec.rb | 63 -- spec/unit/parser/ast/collexpr_spec.rb | 115 ++ spec/unit/parser/ast/collexpr_spec_spec.rb | 115 -- spec/unit/parser/ast/comparison_operator_spec.rb | 92 ++ .../parser/ast/comparison_operator_spec_spec.rb | 92 -- spec/unit/parser/ast/function_spec.rb | 83 ++ spec/unit/parser/ast/function_spec_spec.rb | 83 -- spec/unit/parser/ast/ifstatement_spec.rb | 76 ++ spec/unit/parser/ast/ifstatement_spec_spec.rb | 76 -- spec/unit/parser/ast/in_operator_spec.rb | 60 ++ spec/unit/parser/ast/in_operator_spec_spec.rb | 60 -- spec/unit/parser/ast/leaf_spec.rb | 367 +++++++ spec/unit/parser/ast/leaf_spec_spec.rb | 367 ------- spec/unit/parser/ast/match_operator_spec.rb | 50 + spec/unit/parser/ast/match_operator_spec_spec.rb | 50 - spec/unit/parser/ast/minus_spec.rb | 36 + spec/unit/parser/ast/minus_spec_spec.rb | 36 - spec/unit/parser/ast/nop_spec.rb | 20 + spec/unit/parser/ast/nop_spec_spec.rb | 20 - spec/unit/parser/ast/not_spec.rb | 30 + spec/unit/parser/ast/not_spec_spec.rb | 30 - spec/unit/parser/ast/relationship_spec.rb | 88 ++ spec/unit/parser/ast/relationship_spec_spec.rb | 88 -- spec/unit/parser/ast/resource_defaults_spec.rb | 22 + .../unit/parser/ast/resource_defaults_spec_spec.rb | 22 - spec/unit/parser/ast/resource_override_spec.rb | 51 + .../unit/parser/ast/resource_override_spec_spec.rb | 51 - spec/unit/parser/ast/resource_reference_spec.rb | 46 + .../parser/ast/resource_reference_spec_spec.rb | 46 - spec/unit/parser/ast/resource_spec.rb | 120 +++ spec/unit/parser/ast/resource_spec_spec.rb | 120 --- spec/unit/parser/ast/selector_spec.rb | 139 +++ spec/unit/parser/ast/selector_spec_spec.rb | 139 --- spec/unit/parser/ast/vardef_spec.rb | 60 ++ spec/unit/parser/ast/vardef_spec_spec.rb | 60 -- spec/unit/parser/ast_spec.rb | 41 + spec/unit/parser/ast_spec_spec.rb | 41 - spec/unit/parser/collector_spec.rb | 559 ++++++++++ spec/unit/parser/collector_spec_spec.rb | 559 ---------- spec/unit/parser/compiler_spec.rb | 753 +++++++++++++ spec/unit/parser/compiler_spec_spec.rb | 753 ------------- spec/unit/parser/files_spec.rb | 195 ++++ spec/unit/parser/files_spec_spec.rb | 195 ---- spec/unit/parser/functions/defined_spec.rb | 50 + spec/unit/parser/functions/defined_spec_spec.rb | 50 - spec/unit/parser/functions/fqdn_rand_spec.rb | 62 ++ spec/unit/parser/functions/fqdn_rand_spec_spec.rb | 62 -- spec/unit/parser/functions/generate_spec.rb | 41 + spec/unit/parser/functions/generate_spec_spec.rb | 41 - spec/unit/parser/functions/inline_template_spec.rb | 59 ++ .../parser/functions/inline_template_spec_spec.rb | 59 -- spec/unit/parser/functions/realize_spec.rb | 51 + spec/unit/parser/functions/realize_spec_spec.rb | 51 - spec/unit/parser/functions/regsubst_spec.rb | 168 +++ spec/unit/parser/functions/regsubst_spec_spec.rb | 168 --- spec/unit/parser/functions/require_spec.rb | 74 ++ spec/unit/parser/functions/require_spec_spec.rb | 74 -- spec/unit/parser/functions/shellquote_spec.rb | 92 ++ spec/unit/parser/functions/shellquote_spec_spec.rb | 92 -- spec/unit/parser/functions/split_spec.rb | 51 + spec/unit/parser/functions/split_spec_spec.rb | 51 - spec/unit/parser/functions/sprintf_spec.rb | 42 + spec/unit/parser/functions/sprintf_spec_spec.rb | 42 - spec/unit/parser/functions/tag_spec.rb | 24 + spec/unit/parser/functions/tag_spec_spec.rb | 24 - spec/unit/parser/functions/template_spec.rb | 62 ++ spec/unit/parser/functions/template_spec_spec.rb | 62 -- spec/unit/parser/functions/versioncmp_spec.rb | 29 + spec/unit/parser/functions/versioncmp_spec_spec.rb | 29 - spec/unit/parser/functions_spec.rb | 102 ++ spec/unit/parser/functions_spec_spec.rb | 102 -- spec/unit/parser/lexer_spec.rb | 640 +++++++++++ spec/unit/parser/lexer_spec_spec.rb | 640 ----------- spec/unit/parser/parser_spec.rb | 406 +++++++ spec/unit/parser/parser_spec_spec.rb | 406 ------- spec/unit/parser/relationship_spec.rb | 70 ++ spec/unit/parser/relationship_spec_spec.rb | 70 -- spec/unit/parser/resource_spec.rb | 564 ++++++++++ spec/unit/parser/resource_spec_spec.rb | 564 ---------- spec/unit/parser/scope_spec.rb | 624 +++++++++++ spec/unit/parser/scope_spec_spec.rb | 624 ----------- spec/unit/parser/templatewrapper_spec.rb | 144 +++ spec/unit/parser/templatewrapper_spec_spec.rb | 144 --- spec/unit/parser/type_loader_spec.rb | 201 ++++ spec/unit/parser/type_loader_spec_spec.rb | 201 ---- spec/unit/property/ensure_spec.rb | 13 + spec/unit/property/ensure_spec_spec.rb | 13 - spec/unit/property/keyvalue_spec.rb | 168 +++ spec/unit/property/keyvalue_spec_spec.rb | 168 --- spec/unit/property/list_spec.rb | 166 +++ spec/unit/property/list_spec_spec.rb | 166 --- spec/unit/property/ordered_list_spec.rb | 64 ++ spec/unit/property/ordered_list_spec_spec.rb | 64 -- spec/unit/property_spec.rb | 410 ++++++++ spec/unit/property_spec_spec.rb | 410 -------- spec/unit/provider/augeas/augeas_spec.rb | 413 ++++++++ spec/unit/provider/augeas/augeas_spec_spec.rb | 413 -------- spec/unit/provider/confine/exists_spec.rb | 81 ++ spec/unit/provider/confine/exists_spec_spec.rb | 81 -- spec/unit/provider/confine/false_spec.rb | 53 + spec/unit/provider/confine/false_spec_spec.rb | 53 - spec/unit/provider/confine/feature_spec.rb | 60 ++ spec/unit/provider/confine/feature_spec_spec.rb | 60 -- spec/unit/provider/confine/true_spec.rb | 53 + spec/unit/provider/confine/true_spec_spec.rb | 53 - spec/unit/provider/confine/variable_spec.rb | 107 ++ spec/unit/provider/confine/variable_spec_spec.rb | 107 -- spec/unit/provider/confine_collection_spec.rb | 134 +++ spec/unit/provider/confine_collection_spec_spec.rb | 134 --- spec/unit/provider/confine_spec.rb | 78 ++ spec/unit/provider/confine_spec_spec.rb | 78 -- spec/unit/provider/confiner_spec.rb | 63 ++ spec/unit/provider/confiner_spec_spec.rb | 63 -- spec/unit/provider/group/groupadd_spec.rb | 31 + spec/unit/provider/group/groupadd_spec_spec.rb | 31 - spec/unit/provider/group/ldap_spec.rb | 105 ++ spec/unit/provider/group/ldap_spec_spec.rb | 105 -- spec/unit/provider/ldap_spec.rb | 248 +++++ spec/unit/provider/ldap_spec_spec.rb | 248 ----- spec/unit/provider/macauthorization_spec.rb | 147 +++ spec/unit/provider/macauthorization_spec_spec.rb | 147 --- spec/unit/provider/mcx/mcxcontent_spec.rb | 175 +++ spec/unit/provider/mcx/mcxcontent_spec_spec.rb | 175 --- spec/unit/provider/mount/parsed_spec.rb | 195 ++++ spec/unit/provider/mount/parsed_spec_spec.rb | 195 ---- spec/unit/provider/mount_spec.rb | 130 +++ spec/unit/provider/mount_spec_spec.rb | 130 --- spec/unit/provider/naginator_spec.rb | 58 + spec/unit/provider/naginator_spec_spec.rb | 58 - spec/unit/provider/package/aix_spec.rb | 66 ++ spec/unit/provider/package/aix_spec_spec.rb | 66 -- spec/unit/provider/package/apt_spec.rb | 145 +++ spec/unit/provider/package/apt_spec_spec.rb | 145 --- spec/unit/provider/package/dpkg_spec.rb | 225 ++++ spec/unit/provider/package/dpkg_spec_spec.rb | 225 ---- spec/unit/provider/package/gem_spec.rb | 87 ++ spec/unit/provider/package/gem_spec_spec.rb | 87 -- spec/unit/provider/package/hpux_spec.rb | 52 + spec/unit/provider/package/hpux_spec_spec.rb | 52 - spec/unit/provider/package/nim_spec.rb | 42 + spec/unit/provider/package/nim_spec_spec.rb | 42 - spec/unit/provider/package/pkg_spec.rb | 63 ++ spec/unit/provider/package/pkg_spec_spec.rb | 63 -- spec/unit/provider/package/pkgdmg_spec.rb | 84 ++ spec/unit/provider/package/pkgdmg_spec_spec.rb | 84 -- spec/unit/provider/package/zypper_spec.rb | 81 ++ spec/unit/provider/package/zypper_spec_spec.rb | 81 -- spec/unit/provider/parsedfile_spec.rb | 95 ++ spec/unit/provider/parsedfile_spec_spec.rb | 95 -- spec/unit/provider/selboolean_spec.rb | 37 + spec/unit/provider/selboolean_spec_spec.rb | 37 - spec/unit/provider/selmodule_spec.rb | 66 ++ spec/unit/provider/selmodule_spec_spec.rb | 66 -- spec/unit/provider/service/daemontools_spec.rb | 166 +++ .../unit/provider/service/daemontools_spec_spec.rb | 166 --- spec/unit/provider/service/debian_spec.rb | 89 ++ spec/unit/provider/service/debian_spec_spec.rb | 89 -- spec/unit/provider/service/init_spec.rb | 121 +++ spec/unit/provider/service/init_spec_spec.rb | 121 --- spec/unit/provider/service/launchd_spec.rb | 200 ++++ spec/unit/provider/service/launchd_spec_spec.rb | 200 ---- spec/unit/provider/service/redhat_spec.rb | 96 ++ spec/unit/provider/service/redhat_spec_spec.rb | 96 -- spec/unit/provider/service/runit_spec.rb | 140 +++ spec/unit/provider/service/runit_spec_spec.rb | 140 --- spec/unit/provider/service/src_spec.rb | 97 ++ spec/unit/provider/service/src_spec_spec.rb | 97 -- .../provider/ssh_authorized_key/parsed_spec.rb | 222 ++++ .../ssh_authorized_key/parsed_spec_spec.rb | 222 ---- spec/unit/provider/sshkey/parsed_spec.rb | 38 + spec/unit/provider/sshkey/parsed_spec_spec.rb | 38 - spec/unit/provider/user/hpux_spec.rb | 25 + spec/unit/provider/user/hpux_spec_spec.rb | 25 - spec/unit/provider/user/ldap_spec.rb | 279 +++++ spec/unit/provider/user/ldap_spec_spec.rb | 279 ----- spec/unit/provider/user/user_role_add_spec.rb | 249 +++++ spec/unit/provider/user/user_role_add_spec_spec.rb | 249 ----- spec/unit/provider/user/useradd_spec.rb | 115 ++ spec/unit/provider/user/useradd_spec_spec.rb | 115 -- spec/unit/provider/zfs/solaris_spec.rb | 99 ++ spec/unit/provider/zfs/solaris_spec_spec.rb | 99 -- spec/unit/provider/zone/solaris_spec.rb | 55 + spec/unit/provider/zone/solaris_spec_spec.rb | 55 - spec/unit/provider/zpool/solaris_spec.rb | 179 ++++ spec/unit/provider/zpool/solaris_spec_spec.rb | 179 ---- spec/unit/provider_spec.rb | 31 + spec/unit/provider_spec_spec.rb | 31 - spec/unit/puppet_spec.rb | 12 + spec/unit/puppet_spec_spec.rb | 12 - spec/unit/rails/host_spec.rb | 163 +++ spec/unit/rails/host_spec_spec.rb | 163 --- spec/unit/rails/param_value_spec.rb | 49 + spec/unit/rails/param_value_spec_spec.rb | 49 - spec/unit/rails/resource_spec.rb | 107 ++ spec/unit/rails/resource_spec_spec.rb | 107 -- spec/unit/rails_spec.rb | 258 +++++ spec/unit/rails_spec_spec.rb | 258 ----- spec/unit/relationship_spec.rb | 236 +++++ spec/unit/relationship_spec_spec.rb | 236 ----- spec/unit/reports/http_spec.rb | 56 + spec/unit/reports/http_spec_spec.rb | 56 - spec/unit/reports/tagmail_spec.rb | 94 ++ spec/unit/reports/tagmail_spec_spec.rb | 94 -- spec/unit/reports_spec.rb | 61 ++ spec/unit/reports_spec_spec.rb | 61 -- spec/unit/resource/catalog_spec.rb | 1067 +++++++++++++++++++ spec/unit/resource/catalog_spec_spec.rb | 1067 ------------------- spec/unit/resource/status_spec.rb | 103 ++ spec/unit/resource/status_spec_spec.rb | 103 -- spec/unit/resource/type_collection_helper_spec.rb | 25 + .../resource/type_collection_helper_spec_spec.rb | 25 - spec/unit/resource/type_collection_spec.rb | 432 ++++++++ spec/unit/resource/type_collection_spec_spec.rb | 432 -------- spec/unit/resource/type_spec.rb | 701 ++++++++++++ spec/unit/resource/type_spec_spec.rb | 701 ------------ spec/unit/resource_spec.rb | 805 ++++++++++++++ spec/unit/resource_spec_spec.rb | 805 -------------- spec/unit/run_spec.rb | 134 +++ spec/unit/run_spec_spec.rb | 134 --- spec/unit/simple_graph_spec.rb | 544 ++++++++++ spec/unit/simple_graph_spec_spec.rb | 544 ---------- spec/unit/ssl/base_spec.rb | 43 + spec/unit/ssl/base_spec_spec.rb | 43 - .../ssl/certificate_authority/interface_spec.rb | 333 ++++++ .../certificate_authority/interface_spec_spec.rb | 333 ------ spec/unit/ssl/certificate_authority_spec.rb | 772 ++++++++++++++ spec/unit/ssl/certificate_authority_spec_spec.rb | 772 -------------- spec/unit/ssl/certificate_factory_spec.rb | 107 ++ spec/unit/ssl/certificate_factory_spec_spec.rb | 107 -- spec/unit/ssl/certificate_request_spec.rb | 220 ++++ spec/unit/ssl/certificate_request_spec_spec.rb | 220 ---- spec/unit/ssl/certificate_revocation_list_spec.rb | 168 +++ .../ssl/certificate_revocation_list_spec_spec.rb | 168 --- spec/unit/ssl/certificate_spec.rb | 124 +++ spec/unit/ssl/certificate_spec_spec.rb | 124 --- spec/unit/ssl/host_spec.rb | 707 +++++++++++++ spec/unit/ssl/host_spec_spec.rb | 707 ------------- spec/unit/ssl/inventory_spec.rb | 180 ++++ spec/unit/ssl/inventory_spec_spec.rb | 180 ---- spec/unit/ssl/key_spec.rb | 198 ++++ spec/unit/ssl/key_spec_spec.rb | 198 ---- spec/unit/status_spec.rb | 31 + spec/unit/status_spec_spec.rb | 31 - spec/unit/transaction/change_spec.rb | 193 ++++ spec/unit/transaction/change_spec_spec.rb | 193 ---- spec/unit/transaction/event_manager_spec.rb | 260 +++++ spec/unit/transaction/event_manager_spec_spec.rb | 260 ----- spec/unit/transaction/event_spec.rb | 108 ++ spec/unit/transaction/event_spec_spec.rb | 108 -- spec/unit/transaction/report_spec.rb | 230 ++++ spec/unit/transaction/report_spec_spec.rb | 230 ---- spec/unit/transaction/resource_harness_spec.rb | 401 +++++++ .../unit/transaction/resource_harness_spec_spec.rb | 401 ------- spec/unit/transaction_spec.rb | 452 ++++++++ spec/unit/transaction_spec_spec.rb | 452 -------- spec/unit/transportable_spec.rb | 0 spec/unit/transportable_spec_spec.rb | 0 spec/unit/type/augeas_spec.rb | 122 +++ spec/unit/type/augeas_spec_spec.rb | 122 --- spec/unit/type/component_spec.rb | 63 ++ spec/unit/type/component_spec_spec.rb | 63 -- spec/unit/type/computer_spec.rb | 78 ++ spec/unit/type/computer_spec_spec.rb | 78 -- spec/unit/type/cron_spec.rb | 33 + spec/unit/type/cron_spec_spec.rb | 33 - spec/unit/type/exec_spec.rb | 162 +++ spec/unit/type/exec_spec_spec.rb | 162 --- spec/unit/type/file/checksum_spec.rb | 73 ++ spec/unit/type/file/checksum_spec_spec.rb | 73 -- spec/unit/type/file/content_spec.rb | 459 ++++++++ spec/unit/type/file/content_spec_spec.rb | 459 -------- spec/unit/type/file/ensure_spec.rb | 84 ++ spec/unit/type/file/ensure_spec_spec.rb | 84 -- spec/unit/type/file/group_spec.rb | 123 +++ spec/unit/type/file/group_spec_spec.rb | 123 --- spec/unit/type/file/owner_spec.rb | 150 +++ spec/unit/type/file/owner_spec_spec.rb | 150 --- spec/unit/type/file/selinux_spec.rb | 83 ++ spec/unit/type/file/selinux_spec_spec.rb | 83 -- spec/unit/type/file/source_spec.rb | 272 +++++ spec/unit/type/file/source_spec_spec.rb | 272 ----- spec/unit/type/file_spec.rb | 1053 +++++++++++++++++++ spec/unit/type/file_spec_spec.rb | 1053 ------------------- spec/unit/type/filebucket_spec.rb | 74 ++ spec/unit/type/filebucket_spec_spec.rb | 74 -- spec/unit/type/group_spec.rb | 59 ++ spec/unit/type/group_spec_spec.rb | 59 -- spec/unit/type/macauthorization_spec.rb | 111 ++ spec/unit/type/macauthorization_spec_spec.rb | 111 -- spec/unit/type/maillist_spec.rb | 42 + spec/unit/type/maillist_spec_spec.rb | 42 - spec/unit/type/mcx_spec.rb | 100 ++ spec/unit/type/mcx_spec_spec.rb | 100 -- spec/unit/type/mount_spec.rb | 211 ++++ spec/unit/type/mount_spec_spec.rb | 211 ---- spec/unit/type/nagios_spec.rb | 63 ++ spec/unit/type/nagios_spec_spec.rb | 63 -- spec/unit/type/noop_metaparam_spec.rb | 37 + spec/unit/type/noop_metaparam_spec_spec.rb | 37 - spec/unit/type/package_spec.rb | 236 +++++ spec/unit/type/package_spec_spec.rb | 236 ----- spec/unit/type/resources_spec.rb | 102 ++ spec/unit/type/resources_spec_spec.rb | 102 -- spec/unit/type/schedule_spec.rb | 333 ++++++ spec/unit/type/schedule_spec_spec.rb | 333 ------ spec/unit/type/selboolean_spec.rb | 45 + spec/unit/type/selboolean_spec_spec.rb | 45 - spec/unit/type/selmodule_spec.rb | 18 + spec/unit/type/selmodule_spec_spec.rb | 18 - spec/unit/type/service_spec.rb | 222 ++++ spec/unit/type/service_spec_spec.rb | 222 ---- spec/unit/type/ssh_authorized_key_spec.rb | 140 +++ spec/unit/type/ssh_authorized_key_spec_spec.rb | 140 --- spec/unit/type/stage_spec.rb | 9 + spec/unit/type/stage_spec_spec.rb | 9 - spec/unit/type/tidy_spec.rb | 424 ++++++++ spec/unit/type/tidy_spec_spec.rb | 424 -------- spec/unit/type/user_spec.rb | 283 +++++ spec/unit/type/user_spec_spec.rb | 283 ----- spec/unit/type/zfs_spec.rb | 45 + spec/unit/type/zfs_spec_spec.rb | 45 - spec/unit/type/zone_spec.rb | 63 ++ spec/unit/type/zone_spec_spec.rb | 63 -- spec/unit/type/zpool_spec.rb | 110 ++ spec/unit/type/zpool_spec_spec.rb | 110 -- spec/unit/type_spec.rb | 529 ++++++++++ spec/unit/type_spec_spec.rb | 529 ---------- spec/unit/util/autoload/file_cache_spec.rb | 159 +++ spec/unit/util/autoload/file_cache_spec_spec.rb | 159 --- spec/unit/util/autoload_spec.rb | 120 +++ spec/unit/util/autoload_spec_spec.rb | 120 --- spec/unit/util/backups_spec.rb | 159 +++ spec/unit/util/backups_spec_spec.rb | 159 --- spec/unit/util/cache_accumulator_spec.rb | 69 ++ spec/unit/util/cache_accumulator_spec_spec.rb | 69 -- spec/unit/util/cacher_spec.rb | 185 ++++ spec/unit/util/cacher_spec_spec.rb | 185 ---- spec/unit/util/checksums_spec.rb | 153 +++ spec/unit/util/checksums_spec_spec.rb | 153 --- spec/unit/util/command_line_spec.rb | 108 ++ spec/unit/util/command_line_spec_spec.rb | 108 -- spec/unit/util/constant_inflector_spec.rb | 70 ++ spec/unit/util/constant_inflector_spec_spec.rb | 70 -- spec/unit/util/errors_spec.rb | 38 + spec/unit/util/errors_spec_spec.rb | 38 - spec/unit/util/feature_spec.rb | 72 ++ spec/unit/util/feature_spec_spec.rb | 72 -- spec/unit/util/file_locking_spec.rb | 156 +++ spec/unit/util/file_locking_spec_spec.rb | 156 --- spec/unit/util/filetype_spec.rb | 100 ++ spec/unit/util/filetype_spec_spec.rb | 100 -- spec/unit/util/inline_docs_spec.rb | 32 + spec/unit/util/inline_docs_spec_spec.rb | 32 - spec/unit/util/json_spec.rb | 21 + spec/unit/util/json_spec_spec.rb | 21 - spec/unit/util/ldap/connection_spec.rb | 169 +++ spec/unit/util/ldap/connection_spec_spec.rb | 169 --- spec/unit/util/ldap/generator_spec.rb | 54 + spec/unit/util/ldap/generator_spec_spec.rb | 54 - spec/unit/util/ldap/manager_spec.rb | 654 ++++++++++++ spec/unit/util/ldap/manager_spec_spec.rb | 654 ------------ spec/unit/util/loadedfile_spec.rb | 65 ++ spec/unit/util/loadedfile_spec_spec.rb | 65 -- spec/unit/util/log/destinations_spec.rb | 24 + spec/unit/util/log/destinations_spec_spec.rb | 24 - spec/unit/util/log_spec.rb | 209 ++++ spec/unit/util/log_spec_spec.rb | 209 ---- spec/unit/util/logging_spec.rb | 88 ++ spec/unit/util/logging_spec_spec.rb | 88 -- spec/unit/util/metric_spec.rb | 95 ++ spec/unit/util/metric_spec_spec.rb | 95 -- spec/unit/util/monkey_patches_spec.rb | 7 + spec/unit/util/monkey_patches_spec_spec.rb | 7 - spec/unit/util/nagios_maker_spec.rb | 126 +++ spec/unit/util/nagios_maker_spec_spec.rb | 126 --- spec/unit/util/package_spec.rb | 21 + spec/unit/util/package_spec_spec.rb | 21 - spec/unit/util/posix_spec.rb | 256 +++++ spec/unit/util/posix_spec_spec.rb | 256 ----- spec/unit/util/queue/stomp_spec.rb | 140 +++ spec/unit/util/queue/stomp_spec_spec.rb | 140 --- spec/unit/util/queue_spec.rb | 96 ++ spec/unit/util/queue_spec_spec.rb | 96 -- spec/unit/util/rdoc/parser_spec.rb | 540 ++++++++++ spec/unit/util/rdoc/parser_spec_spec.rb | 540 ---------- spec/unit/util/rdoc_spec.rb | 165 +++ spec/unit/util/rdoc_spec_spec.rb | 165 --- spec/unit/util/reference_serializer_spec.rb | 52 + spec/unit/util/reference_serializer_spec_spec.rb | 52 - spec/unit/util/resource_template_spec.rb | 58 + spec/unit/util/resource_template_spec_spec.rb | 58 - spec/unit/util/selinux_spec.rb | 280 +++++ spec/unit/util/selinux_spec_spec.rb | 280 ----- spec/unit/util/settings/file_setting_spec.rb | 256 +++++ spec/unit/util/settings/file_setting_spec_spec.rb | 256 ----- spec/unit/util/settings_spec.rb | 1111 ++++++++++++++++++++ spec/unit/util/settings_spec_spec.rb | 1111 -------------------- spec/unit/util/storage_spec.rb | 235 +++++ spec/unit/util/storage_spec_spec.rb | 235 ----- spec/unit/util/tagging_spec.rb | 102 ++ spec/unit/util/tagging_spec_spec.rb | 102 -- spec/unit/util/user_attr_spec.rb | 47 + spec/unit/util/user_attr_spec_spec.rb | 47 - spec/unit/util/warnings_spec.rb | 39 + spec/unit/util/warnings_spec_spec.rb | 39 - spec/unit/util/zaml_spec.rb | 38 + spec/unit/util/zaml_spec_spec.rb | 38 - 688 files changed, 56308 insertions(+), 56308 deletions(-) create mode 100755 spec/unit/agent/locker_spec.rb delete mode 100755 spec/unit/agent/locker_spec_spec.rb create mode 100755 spec/unit/agent_spec.rb delete mode 100755 spec/unit/agent_spec_spec.rb create mode 100755 spec/unit/application/agent_spec.rb delete mode 100755 spec/unit/application/agent_spec_spec.rb create mode 100755 spec/unit/application/apply_spec.rb delete mode 100755 spec/unit/application/apply_spec_spec.rb create mode 100644 spec/unit/application/cert_spec.rb delete mode 100644 spec/unit/application/cert_spec_spec.rb create mode 100755 spec/unit/application/describe_spec.rb delete mode 100755 spec/unit/application/describe_spec_spec.rb create mode 100755 spec/unit/application/doc_spec.rb delete mode 100755 spec/unit/application/doc_spec_spec.rb create mode 100644 spec/unit/application/filebucket_spec.rb delete mode 100644 spec/unit/application/filebucket_spec_spec.rb create mode 100755 spec/unit/application/kick_spec.rb delete mode 100755 spec/unit/application/kick_spec_spec.rb create mode 100644 spec/unit/application/master_spec.rb delete mode 100644 spec/unit/application/master_spec_spec.rb create mode 100755 spec/unit/application/queue_spec.rb delete mode 100755 spec/unit/application/queue_spec_spec.rb create mode 100755 spec/unit/application/resource_spec.rb delete mode 100755 spec/unit/application/resource_spec_spec.rb create mode 100755 spec/unit/application_spec.rb delete mode 100755 spec/unit/application_spec_spec.rb create mode 100755 spec/unit/configurer/downloader_spec.rb delete mode 100755 spec/unit/configurer/downloader_spec_spec.rb create mode 100755 spec/unit/configurer/fact_handler_spec.rb delete mode 100755 spec/unit/configurer/fact_handler_spec_spec.rb create mode 100755 spec/unit/configurer/plugin_handler_spec.rb delete mode 100755 spec/unit/configurer/plugin_handler_spec_spec.rb create mode 100755 spec/unit/configurer_spec.rb delete mode 100755 spec/unit/configurer_spec_spec.rb create mode 100755 spec/unit/daemon_spec.rb delete mode 100755 spec/unit/daemon_spec_spec.rb create mode 100755 spec/unit/dsl/resource_api_spec.rb delete mode 100755 spec/unit/dsl/resource_api_spec_spec.rb create mode 100755 spec/unit/dsl/resource_type_api_spec.rb delete mode 100755 spec/unit/dsl/resource_type_api_spec_spec.rb create mode 100755 spec/unit/file_bucket/dipper_spec.rb delete mode 100755 spec/unit/file_bucket/dipper_spec_spec.rb create mode 100644 spec/unit/file_bucket/file_spec.rb delete mode 100644 spec/unit/file_bucket/file_spec_spec.rb create mode 100755 spec/unit/file_collection/lookup_spec.rb delete mode 100755 spec/unit/file_collection/lookup_spec_spec.rb create mode 100755 spec/unit/file_collection_spec.rb delete mode 100755 spec/unit/file_collection_spec_spec.rb create mode 100755 spec/unit/file_serving/base_spec.rb delete mode 100755 spec/unit/file_serving/base_spec_spec.rb create mode 100755 spec/unit/file_serving/configuration/parser_spec.rb delete mode 100755 spec/unit/file_serving/configuration/parser_spec_spec.rb create mode 100755 spec/unit/file_serving/configuration_spec.rb delete mode 100755 spec/unit/file_serving/configuration_spec_spec.rb create mode 100755 spec/unit/file_serving/content_spec.rb delete mode 100755 spec/unit/file_serving/content_spec_spec.rb create mode 100755 spec/unit/file_serving/fileset_spec.rb delete mode 100755 spec/unit/file_serving/fileset_spec_spec.rb create mode 100755 spec/unit/file_serving/indirection_hooks_spec.rb delete mode 100755 spec/unit/file_serving/indirection_hooks_spec_spec.rb create mode 100755 spec/unit/file_serving/metadata_spec.rb delete mode 100755 spec/unit/file_serving/metadata_spec_spec.rb create mode 100755 spec/unit/file_serving/mount/file_spec.rb delete mode 100755 spec/unit/file_serving/mount/file_spec_spec.rb create mode 100755 spec/unit/file_serving/mount/modules_spec.rb delete mode 100755 spec/unit/file_serving/mount/modules_spec_spec.rb create mode 100755 spec/unit/file_serving/mount/plugins_spec.rb delete mode 100755 spec/unit/file_serving/mount/plugins_spec_spec.rb create mode 100755 spec/unit/file_serving/mount_spec.rb delete mode 100755 spec/unit/file_serving/mount_spec_spec.rb create mode 100755 spec/unit/file_serving/terminus_helper_spec.rb delete mode 100755 spec/unit/file_serving/terminus_helper_spec_spec.rb create mode 100755 spec/unit/indirector/active_record_spec.rb delete mode 100755 spec/unit/indirector/active_record_spec_spec.rb create mode 100755 spec/unit/indirector/catalog/active_record_spec.rb delete mode 100755 spec/unit/indirector/catalog/active_record_spec_spec.rb create mode 100755 spec/unit/indirector/catalog/compiler_spec.rb delete mode 100755 spec/unit/indirector/catalog/compiler_spec_spec.rb create mode 100755 spec/unit/indirector/catalog/queue_spec.rb delete mode 100755 spec/unit/indirector/catalog/queue_spec_spec.rb create mode 100755 spec/unit/indirector/catalog/rest_spec.rb delete mode 100755 spec/unit/indirector/catalog/rest_spec_spec.rb create mode 100755 spec/unit/indirector/catalog/yaml_spec.rb delete mode 100755 spec/unit/indirector/catalog/yaml_spec_spec.rb create mode 100755 spec/unit/indirector/certificate/ca_spec.rb delete mode 100755 spec/unit/indirector/certificate/ca_spec_spec.rb create mode 100755 spec/unit/indirector/certificate/file_spec.rb delete mode 100755 spec/unit/indirector/certificate/file_spec_spec.rb create mode 100755 spec/unit/indirector/certificate/rest_spec.rb delete mode 100755 spec/unit/indirector/certificate/rest_spec_spec.rb create mode 100755 spec/unit/indirector/certificate_request/ca_spec.rb delete mode 100755 spec/unit/indirector/certificate_request/ca_spec_spec.rb create mode 100755 spec/unit/indirector/certificate_request/file_spec.rb delete mode 100755 spec/unit/indirector/certificate_request/file_spec_spec.rb create mode 100755 spec/unit/indirector/certificate_request/rest_spec.rb delete mode 100755 spec/unit/indirector/certificate_request/rest_spec_spec.rb create mode 100755 spec/unit/indirector/certificate_revocation_list/ca_spec.rb delete mode 100755 spec/unit/indirector/certificate_revocation_list/ca_spec_spec.rb create mode 100755 spec/unit/indirector/certificate_revocation_list/file_spec.rb delete mode 100755 spec/unit/indirector/certificate_revocation_list/file_spec_spec.rb create mode 100755 spec/unit/indirector/certificate_revocation_list/rest_spec.rb delete mode 100755 spec/unit/indirector/certificate_revocation_list/rest_spec_spec.rb create mode 100755 spec/unit/indirector/code_spec.rb delete mode 100755 spec/unit/indirector/code_spec_spec.rb create mode 100755 spec/unit/indirector/direct_file_server_spec.rb delete mode 100755 spec/unit/indirector/direct_file_server_spec_spec.rb create mode 100755 spec/unit/indirector/envelope_spec.rb delete mode 100755 spec/unit/indirector/envelope_spec_spec.rb create mode 100755 spec/unit/indirector/exec_spec.rb delete mode 100755 spec/unit/indirector/exec_spec_spec.rb create mode 100755 spec/unit/indirector/facts/active_record_spec.rb delete mode 100755 spec/unit/indirector/facts/active_record_spec_spec.rb create mode 100644 spec/unit/indirector/facts/couch_spec.rb delete mode 100644 spec/unit/indirector/facts/couch_spec_spec.rb create mode 100755 spec/unit/indirector/facts/facter_spec.rb delete mode 100755 spec/unit/indirector/facts/facter_spec_spec.rb create mode 100755 spec/unit/indirector/facts/rest_spec.rb delete mode 100755 spec/unit/indirector/facts/rest_spec_spec.rb create mode 100755 spec/unit/indirector/facts/yaml_spec.rb delete mode 100755 spec/unit/indirector/facts/yaml_spec_spec.rb create mode 100755 spec/unit/indirector/file_bucket_file/file_spec.rb delete mode 100755 spec/unit/indirector/file_bucket_file/file_spec_spec.rb create mode 100755 spec/unit/indirector/file_bucket_file/rest_spec.rb delete mode 100755 spec/unit/indirector/file_bucket_file/rest_spec_spec.rb create mode 100755 spec/unit/indirector/file_content/file_server_spec.rb delete mode 100755 spec/unit/indirector/file_content/file_server_spec_spec.rb create mode 100755 spec/unit/indirector/file_content/file_spec.rb delete mode 100755 spec/unit/indirector/file_content/file_spec_spec.rb create mode 100755 spec/unit/indirector/file_content/rest_spec.rb delete mode 100755 spec/unit/indirector/file_content/rest_spec_spec.rb create mode 100755 spec/unit/indirector/file_metadata/file_server_spec.rb delete mode 100755 spec/unit/indirector/file_metadata/file_server_spec_spec.rb create mode 100755 spec/unit/indirector/file_metadata/file_spec.rb delete mode 100755 spec/unit/indirector/file_metadata/file_spec_spec.rb create mode 100755 spec/unit/indirector/file_metadata/rest_spec.rb delete mode 100755 spec/unit/indirector/file_metadata/rest_spec_spec.rb create mode 100755 spec/unit/indirector/file_server_spec.rb delete mode 100755 spec/unit/indirector/file_server_spec_spec.rb create mode 100755 spec/unit/indirector/file_spec.rb delete mode 100755 spec/unit/indirector/file_spec_spec.rb create mode 100755 spec/unit/indirector/indirection_spec.rb delete mode 100755 spec/unit/indirector/indirection_spec_spec.rb create mode 100755 spec/unit/indirector/key/ca_spec.rb delete mode 100755 spec/unit/indirector/key/ca_spec_spec.rb create mode 100755 spec/unit/indirector/key/file_spec.rb delete mode 100755 spec/unit/indirector/key/file_spec_spec.rb create mode 100755 spec/unit/indirector/ldap_spec.rb delete mode 100755 spec/unit/indirector/ldap_spec_spec.rb create mode 100755 spec/unit/indirector/memory_spec.rb delete mode 100755 spec/unit/indirector/memory_spec_spec.rb create mode 100755 spec/unit/indirector/node/active_record_spec.rb delete mode 100755 spec/unit/indirector/node/active_record_spec_spec.rb create mode 100755 spec/unit/indirector/node/exec_spec.rb delete mode 100755 spec/unit/indirector/node/exec_spec_spec.rb create mode 100755 spec/unit/indirector/node/ldap_spec.rb delete mode 100755 spec/unit/indirector/node/ldap_spec_spec.rb create mode 100755 spec/unit/indirector/node/memory_spec.rb delete mode 100755 spec/unit/indirector/node/memory_spec_spec.rb create mode 100755 spec/unit/indirector/node/plain_spec.rb delete mode 100755 spec/unit/indirector/node/plain_spec_spec.rb create mode 100755 spec/unit/indirector/node/rest_spec.rb delete mode 100755 spec/unit/indirector/node/rest_spec_spec.rb create mode 100755 spec/unit/indirector/node/yaml_spec.rb delete mode 100755 spec/unit/indirector/node/yaml_spec_spec.rb create mode 100755 spec/unit/indirector/plain_spec.rb delete mode 100755 spec/unit/indirector/plain_spec_spec.rb create mode 100755 spec/unit/indirector/queue_spec.rb delete mode 100755 spec/unit/indirector/queue_spec_spec.rb create mode 100755 spec/unit/indirector/report/processor_spec.rb delete mode 100755 spec/unit/indirector/report/processor_spec_spec.rb create mode 100755 spec/unit/indirector/report/rest_spec.rb delete mode 100755 spec/unit/indirector/report/rest_spec_spec.rb create mode 100755 spec/unit/indirector/request_spec.rb delete mode 100755 spec/unit/indirector/request_spec_spec.rb create mode 100644 spec/unit/indirector/resource/ral_spec.rb delete mode 100644 spec/unit/indirector/resource/ral_spec_spec.rb create mode 100755 spec/unit/indirector/resource/rest_spec.rb delete mode 100755 spec/unit/indirector/resource/rest_spec_spec.rb create mode 100755 spec/unit/indirector/resource_type/parser_spec.rb delete mode 100755 spec/unit/indirector/resource_type/parser_spec_spec.rb create mode 100755 spec/unit/indirector/resource_type/rest_spec.rb delete mode 100755 spec/unit/indirector/resource_type/rest_spec_spec.rb create mode 100755 spec/unit/indirector/rest_spec.rb delete mode 100755 spec/unit/indirector/rest_spec_spec.rb create mode 100644 spec/unit/indirector/run/local_spec.rb delete mode 100644 spec/unit/indirector/run/local_spec_spec.rb create mode 100755 spec/unit/indirector/run/rest_spec.rb delete mode 100755 spec/unit/indirector/run/rest_spec_spec.rb create mode 100755 spec/unit/indirector/ssl_file_spec.rb delete mode 100755 spec/unit/indirector/ssl_file_spec_spec.rb create mode 100755 spec/unit/indirector/status/rest_spec.rb delete mode 100755 spec/unit/indirector/status/rest_spec_spec.rb create mode 100755 spec/unit/indirector/terminus_spec.rb delete mode 100755 spec/unit/indirector/terminus_spec_spec.rb create mode 100755 spec/unit/indirector/yaml_spec.rb delete mode 100755 spec/unit/indirector/yaml_spec_spec.rb create mode 100755 spec/unit/indirector_spec.rb delete mode 100755 spec/unit/indirector_spec_spec.rb create mode 100755 spec/unit/module_spec.rb delete mode 100755 spec/unit/module_spec_spec.rb create mode 100755 spec/unit/network/authconfig_spec.rb delete mode 100755 spec/unit/network/authconfig_spec_spec.rb create mode 100644 spec/unit/network/authstore_spec.rb delete mode 100644 spec/unit/network/authstore_spec_spec.rb create mode 100755 spec/unit/network/client_spec.rb delete mode 100755 spec/unit/network/client_spec_spec.rb create mode 100755 spec/unit/network/format_handler_spec.rb delete mode 100755 spec/unit/network/format_handler_spec_spec.rb create mode 100755 spec/unit/network/format_spec.rb delete mode 100755 spec/unit/network/format_spec_spec.rb create mode 100755 spec/unit/network/formats_spec.rb delete mode 100755 spec/unit/network/formats_spec_spec.rb create mode 100644 spec/unit/network/handler/fileserver_spec.rb delete mode 100644 spec/unit/network/handler/fileserver_spec_spec.rb create mode 100644 spec/unit/network/http/api/v1_spec.rb delete mode 100644 spec/unit/network/http/api/v1_spec_spec.rb create mode 100644 spec/unit/network/http/compression_spec.rb delete mode 100644 spec/unit/network/http/compression_spec_spec.rb create mode 100755 spec/unit/network/http/handler_spec.rb delete mode 100755 spec/unit/network/http/handler_spec_spec.rb create mode 100755 spec/unit/network/http/mongrel/rest_spec.rb delete mode 100755 spec/unit/network/http/mongrel/rest_spec_spec.rb create mode 100755 spec/unit/network/http/mongrel/xmlrpc_spec.rb delete mode 100755 spec/unit/network/http/mongrel/xmlrpc_spec_spec.rb create mode 100755 spec/unit/network/http/mongrel_spec.rb delete mode 100755 spec/unit/network/http/mongrel_spec_spec.rb create mode 100755 spec/unit/network/http/rack/rest_spec.rb delete mode 100755 spec/unit/network/http/rack/rest_spec_spec.rb create mode 100755 spec/unit/network/http/rack/xmlrpc_spec.rb delete mode 100755 spec/unit/network/http/rack/xmlrpc_spec_spec.rb create mode 100755 spec/unit/network/http/rack_spec.rb delete mode 100755 spec/unit/network/http/rack_spec_spec.rb create mode 100755 spec/unit/network/http/webrick/rest_spec.rb delete mode 100755 spec/unit/network/http/webrick/rest_spec_spec.rb create mode 100755 spec/unit/network/http/webrick/xmlrpc_spec.rb delete mode 100755 spec/unit/network/http/webrick/xmlrpc_spec_spec.rb create mode 100755 spec/unit/network/http/webrick_spec.rb delete mode 100755 spec/unit/network/http/webrick_spec_spec.rb create mode 100755 spec/unit/network/http_pool_spec.rb delete mode 100755 spec/unit/network/http_pool_spec_spec.rb create mode 100755 spec/unit/network/http_spec.rb delete mode 100755 spec/unit/network/http_spec_spec.rb create mode 100755 spec/unit/network/rest_authconfig_spec.rb delete mode 100755 spec/unit/network/rest_authconfig_spec_spec.rb create mode 100755 spec/unit/network/rest_authorization_spec.rb delete mode 100755 spec/unit/network/rest_authorization_spec_spec.rb create mode 100755 spec/unit/network/rights_spec.rb delete mode 100755 spec/unit/network/rights_spec_spec.rb create mode 100755 spec/unit/network/server_spec.rb delete mode 100755 spec/unit/network/server_spec_spec.rb create mode 100755 spec/unit/network/xmlrpc/client_spec.rb delete mode 100755 spec/unit/network/xmlrpc/client_spec_spec.rb create mode 100755 spec/unit/node/environment_spec.rb delete mode 100755 spec/unit/node/environment_spec_spec.rb create mode 100755 spec/unit/node/facts_spec.rb delete mode 100755 spec/unit/node/facts_spec_spec.rb create mode 100755 spec/unit/node_spec.rb delete mode 100755 spec/unit/node_spec_spec.rb create mode 100644 spec/unit/other/selinux_spec.rb delete mode 100644 spec/unit/other/selinux_spec_spec.rb create mode 100755 spec/unit/other/transbucket_spec.rb delete mode 100755 spec/unit/other/transbucket_spec_spec.rb create mode 100755 spec/unit/other/transobject_spec.rb delete mode 100755 spec/unit/other/transobject_spec_spec.rb create mode 100755 spec/unit/parameter/value_collection_spec.rb delete mode 100755 spec/unit/parameter/value_collection_spec_spec.rb create mode 100755 spec/unit/parameter/value_spec.rb delete mode 100755 spec/unit/parameter/value_spec_spec.rb create mode 100755 spec/unit/parameter_spec.rb delete mode 100755 spec/unit/parameter_spec_spec.rb create mode 100755 spec/unit/parser/ast/arithmetic_operator_spec.rb delete mode 100755 spec/unit/parser/ast/arithmetic_operator_spec_spec.rb create mode 100755 spec/unit/parser/ast/astarray_spec.rb delete mode 100755 spec/unit/parser/ast/astarray_spec_spec.rb create mode 100644 spec/unit/parser/ast/asthash_spec.rb delete mode 100644 spec/unit/parser/ast/asthash_spec_spec.rb create mode 100755 spec/unit/parser/ast/boolean_operator_spec.rb delete mode 100755 spec/unit/parser/ast/boolean_operator_spec_spec.rb create mode 100755 spec/unit/parser/ast/casestatement_spec.rb delete mode 100755 spec/unit/parser/ast/casestatement_spec_spec.rb create mode 100755 spec/unit/parser/ast/collection_spec.rb delete mode 100755 spec/unit/parser/ast/collection_spec_spec.rb create mode 100755 spec/unit/parser/ast/collexpr_spec.rb delete mode 100755 spec/unit/parser/ast/collexpr_spec_spec.rb create mode 100755 spec/unit/parser/ast/comparison_operator_spec.rb delete mode 100755 spec/unit/parser/ast/comparison_operator_spec_spec.rb create mode 100644 spec/unit/parser/ast/function_spec.rb delete mode 100644 spec/unit/parser/ast/function_spec_spec.rb create mode 100755 spec/unit/parser/ast/ifstatement_spec.rb delete mode 100755 spec/unit/parser/ast/ifstatement_spec_spec.rb create mode 100644 spec/unit/parser/ast/in_operator_spec.rb delete mode 100644 spec/unit/parser/ast/in_operator_spec_spec.rb create mode 100755 spec/unit/parser/ast/leaf_spec.rb delete mode 100755 spec/unit/parser/ast/leaf_spec_spec.rb create mode 100755 spec/unit/parser/ast/match_operator_spec.rb delete mode 100755 spec/unit/parser/ast/match_operator_spec_spec.rb create mode 100755 spec/unit/parser/ast/minus_spec.rb delete mode 100755 spec/unit/parser/ast/minus_spec_spec.rb create mode 100755 spec/unit/parser/ast/nop_spec.rb delete mode 100755 spec/unit/parser/ast/nop_spec_spec.rb create mode 100755 spec/unit/parser/ast/not_spec.rb delete mode 100755 spec/unit/parser/ast/not_spec_spec.rb create mode 100644 spec/unit/parser/ast/relationship_spec.rb delete mode 100644 spec/unit/parser/ast/relationship_spec_spec.rb create mode 100755 spec/unit/parser/ast/resource_defaults_spec.rb delete mode 100755 spec/unit/parser/ast/resource_defaults_spec_spec.rb create mode 100755 spec/unit/parser/ast/resource_override_spec.rb delete mode 100755 spec/unit/parser/ast/resource_override_spec_spec.rb create mode 100755 spec/unit/parser/ast/resource_reference_spec.rb delete mode 100755 spec/unit/parser/ast/resource_reference_spec_spec.rb create mode 100755 spec/unit/parser/ast/resource_spec.rb delete mode 100755 spec/unit/parser/ast/resource_spec_spec.rb create mode 100755 spec/unit/parser/ast/selector_spec.rb delete mode 100755 spec/unit/parser/ast/selector_spec_spec.rb create mode 100755 spec/unit/parser/ast/vardef_spec.rb delete mode 100755 spec/unit/parser/ast/vardef_spec_spec.rb create mode 100644 spec/unit/parser/ast_spec.rb delete mode 100644 spec/unit/parser/ast_spec_spec.rb create mode 100755 spec/unit/parser/collector_spec.rb delete mode 100755 spec/unit/parser/collector_spec_spec.rb create mode 100755 spec/unit/parser/compiler_spec.rb delete mode 100755 spec/unit/parser/compiler_spec_spec.rb create mode 100644 spec/unit/parser/files_spec.rb delete mode 100644 spec/unit/parser/files_spec_spec.rb create mode 100755 spec/unit/parser/functions/defined_spec.rb delete mode 100755 spec/unit/parser/functions/defined_spec_spec.rb create mode 100644 spec/unit/parser/functions/fqdn_rand_spec.rb delete mode 100644 spec/unit/parser/functions/fqdn_rand_spec_spec.rb create mode 100755 spec/unit/parser/functions/generate_spec.rb delete mode 100755 spec/unit/parser/functions/generate_spec_spec.rb create mode 100755 spec/unit/parser/functions/inline_template_spec.rb delete mode 100755 spec/unit/parser/functions/inline_template_spec_spec.rb create mode 100755 spec/unit/parser/functions/realize_spec.rb delete mode 100755 spec/unit/parser/functions/realize_spec_spec.rb create mode 100755 spec/unit/parser/functions/regsubst_spec.rb delete mode 100755 spec/unit/parser/functions/regsubst_spec_spec.rb create mode 100755 spec/unit/parser/functions/require_spec.rb delete mode 100755 spec/unit/parser/functions/require_spec_spec.rb create mode 100755 spec/unit/parser/functions/shellquote_spec.rb delete mode 100755 spec/unit/parser/functions/shellquote_spec_spec.rb create mode 100755 spec/unit/parser/functions/split_spec.rb delete mode 100755 spec/unit/parser/functions/split_spec_spec.rb create mode 100755 spec/unit/parser/functions/sprintf_spec.rb delete mode 100755 spec/unit/parser/functions/sprintf_spec_spec.rb create mode 100755 spec/unit/parser/functions/tag_spec.rb delete mode 100755 spec/unit/parser/functions/tag_spec_spec.rb create mode 100755 spec/unit/parser/functions/template_spec.rb delete mode 100755 spec/unit/parser/functions/template_spec_spec.rb create mode 100755 spec/unit/parser/functions/versioncmp_spec.rb delete mode 100755 spec/unit/parser/functions/versioncmp_spec_spec.rb create mode 100644 spec/unit/parser/functions_spec.rb delete mode 100644 spec/unit/parser/functions_spec_spec.rb create mode 100755 spec/unit/parser/lexer_spec.rb delete mode 100755 spec/unit/parser/lexer_spec_spec.rb create mode 100755 spec/unit/parser/parser_spec.rb delete mode 100755 spec/unit/parser/parser_spec_spec.rb create mode 100644 spec/unit/parser/relationship_spec.rb delete mode 100644 spec/unit/parser/relationship_spec_spec.rb create mode 100755 spec/unit/parser/resource_spec.rb delete mode 100755 spec/unit/parser/resource_spec_spec.rb create mode 100755 spec/unit/parser/scope_spec.rb delete mode 100755 spec/unit/parser/scope_spec_spec.rb create mode 100755 spec/unit/parser/templatewrapper_spec.rb delete mode 100755 spec/unit/parser/templatewrapper_spec_spec.rb create mode 100644 spec/unit/parser/type_loader_spec.rb delete mode 100644 spec/unit/parser/type_loader_spec_spec.rb create mode 100644 spec/unit/property/ensure_spec.rb delete mode 100644 spec/unit/property/ensure_spec_spec.rb create mode 100644 spec/unit/property/keyvalue_spec.rb delete mode 100644 spec/unit/property/keyvalue_spec_spec.rb create mode 100644 spec/unit/property/list_spec.rb delete mode 100644 spec/unit/property/list_spec_spec.rb create mode 100644 spec/unit/property/ordered_list_spec.rb delete mode 100644 spec/unit/property/ordered_list_spec_spec.rb create mode 100755 spec/unit/property_spec.rb delete mode 100755 spec/unit/property_spec_spec.rb create mode 100644 spec/unit/provider/augeas/augeas_spec.rb delete mode 100644 spec/unit/provider/augeas/augeas_spec_spec.rb create mode 100755 spec/unit/provider/confine/exists_spec.rb delete mode 100755 spec/unit/provider/confine/exists_spec_spec.rb create mode 100755 spec/unit/provider/confine/false_spec.rb delete mode 100755 spec/unit/provider/confine/false_spec_spec.rb create mode 100755 spec/unit/provider/confine/feature_spec.rb delete mode 100755 spec/unit/provider/confine/feature_spec_spec.rb create mode 100755 spec/unit/provider/confine/true_spec.rb delete mode 100755 spec/unit/provider/confine/true_spec_spec.rb create mode 100755 spec/unit/provider/confine/variable_spec.rb delete mode 100755 spec/unit/provider/confine/variable_spec_spec.rb create mode 100755 spec/unit/provider/confine_collection_spec.rb delete mode 100755 spec/unit/provider/confine_collection_spec_spec.rb create mode 100755 spec/unit/provider/confine_spec.rb delete mode 100755 spec/unit/provider/confine_spec_spec.rb create mode 100755 spec/unit/provider/confiner_spec.rb delete mode 100755 spec/unit/provider/confiner_spec_spec.rb create mode 100755 spec/unit/provider/group/groupadd_spec.rb delete mode 100755 spec/unit/provider/group/groupadd_spec_spec.rb create mode 100755 spec/unit/provider/group/ldap_spec.rb delete mode 100755 spec/unit/provider/group/ldap_spec_spec.rb create mode 100755 spec/unit/provider/ldap_spec.rb delete mode 100755 spec/unit/provider/ldap_spec_spec.rb create mode 100755 spec/unit/provider/macauthorization_spec.rb delete mode 100755 spec/unit/provider/macauthorization_spec_spec.rb create mode 100755 spec/unit/provider/mcx/mcxcontent_spec.rb delete mode 100755 spec/unit/provider/mcx/mcxcontent_spec_spec.rb create mode 100755 spec/unit/provider/mount/parsed_spec.rb delete mode 100755 spec/unit/provider/mount/parsed_spec_spec.rb create mode 100755 spec/unit/provider/mount_spec.rb delete mode 100755 spec/unit/provider/mount_spec_spec.rb create mode 100755 spec/unit/provider/naginator_spec.rb delete mode 100755 spec/unit/provider/naginator_spec_spec.rb create mode 100755 spec/unit/provider/package/aix_spec.rb delete mode 100755 spec/unit/provider/package/aix_spec_spec.rb create mode 100755 spec/unit/provider/package/apt_spec.rb delete mode 100755 spec/unit/provider/package/apt_spec_spec.rb create mode 100755 spec/unit/provider/package/dpkg_spec.rb delete mode 100755 spec/unit/provider/package/dpkg_spec_spec.rb create mode 100644 spec/unit/provider/package/gem_spec.rb delete mode 100644 spec/unit/provider/package/gem_spec_spec.rb create mode 100644 spec/unit/provider/package/hpux_spec.rb delete mode 100644 spec/unit/provider/package/hpux_spec_spec.rb create mode 100755 spec/unit/provider/package/nim_spec.rb delete mode 100755 spec/unit/provider/package/nim_spec_spec.rb create mode 100644 spec/unit/provider/package/pkg_spec.rb delete mode 100644 spec/unit/provider/package/pkg_spec_spec.rb create mode 100755 spec/unit/provider/package/pkgdmg_spec.rb delete mode 100755 spec/unit/provider/package/pkgdmg_spec_spec.rb create mode 100644 spec/unit/provider/package/zypper_spec.rb delete mode 100644 spec/unit/provider/package/zypper_spec_spec.rb create mode 100755 spec/unit/provider/parsedfile_spec.rb delete mode 100755 spec/unit/provider/parsedfile_spec_spec.rb create mode 100755 spec/unit/provider/selboolean_spec.rb delete mode 100755 spec/unit/provider/selboolean_spec_spec.rb create mode 100755 spec/unit/provider/selmodule_spec.rb delete mode 100755 spec/unit/provider/selmodule_spec_spec.rb create mode 100755 spec/unit/provider/service/daemontools_spec.rb delete mode 100755 spec/unit/provider/service/daemontools_spec_spec.rb create mode 100755 spec/unit/provider/service/debian_spec.rb delete mode 100755 spec/unit/provider/service/debian_spec_spec.rb create mode 100755 spec/unit/provider/service/init_spec.rb delete mode 100755 spec/unit/provider/service/init_spec_spec.rb create mode 100755 spec/unit/provider/service/launchd_spec.rb delete mode 100755 spec/unit/provider/service/launchd_spec_spec.rb create mode 100755 spec/unit/provider/service/redhat_spec.rb delete mode 100755 spec/unit/provider/service/redhat_spec_spec.rb create mode 100755 spec/unit/provider/service/runit_spec.rb delete mode 100755 spec/unit/provider/service/runit_spec_spec.rb create mode 100755 spec/unit/provider/service/src_spec.rb delete mode 100755 spec/unit/provider/service/src_spec_spec.rb create mode 100755 spec/unit/provider/ssh_authorized_key/parsed_spec.rb delete mode 100755 spec/unit/provider/ssh_authorized_key/parsed_spec_spec.rb create mode 100755 spec/unit/provider/sshkey/parsed_spec.rb delete mode 100755 spec/unit/provider/sshkey/parsed_spec_spec.rb create mode 100755 spec/unit/provider/user/hpux_spec.rb delete mode 100755 spec/unit/provider/user/hpux_spec_spec.rb create mode 100755 spec/unit/provider/user/ldap_spec.rb delete mode 100755 spec/unit/provider/user/ldap_spec_spec.rb create mode 100644 spec/unit/provider/user/user_role_add_spec.rb delete mode 100644 spec/unit/provider/user/user_role_add_spec_spec.rb create mode 100755 spec/unit/provider/user/useradd_spec.rb delete mode 100755 spec/unit/provider/user/useradd_spec_spec.rb create mode 100755 spec/unit/provider/zfs/solaris_spec.rb delete mode 100755 spec/unit/provider/zfs/solaris_spec_spec.rb create mode 100755 spec/unit/provider/zone/solaris_spec.rb delete mode 100755 spec/unit/provider/zone/solaris_spec_spec.rb create mode 100755 spec/unit/provider/zpool/solaris_spec.rb delete mode 100755 spec/unit/provider/zpool/solaris_spec_spec.rb create mode 100755 spec/unit/provider_spec.rb delete mode 100755 spec/unit/provider_spec_spec.rb create mode 100755 spec/unit/puppet_spec.rb delete mode 100755 spec/unit/puppet_spec_spec.rb create mode 100755 spec/unit/rails/host_spec.rb delete mode 100755 spec/unit/rails/host_spec_spec.rb create mode 100755 spec/unit/rails/param_value_spec.rb delete mode 100755 spec/unit/rails/param_value_spec_spec.rb create mode 100755 spec/unit/rails/resource_spec.rb delete mode 100755 spec/unit/rails/resource_spec_spec.rb create mode 100755 spec/unit/rails_spec.rb delete mode 100755 spec/unit/rails_spec_spec.rb create mode 100755 spec/unit/relationship_spec.rb delete mode 100755 spec/unit/relationship_spec_spec.rb create mode 100644 spec/unit/reports/http_spec.rb delete mode 100644 spec/unit/reports/http_spec_spec.rb create mode 100755 spec/unit/reports/tagmail_spec.rb delete mode 100755 spec/unit/reports/tagmail_spec_spec.rb create mode 100755 spec/unit/reports_spec.rb delete mode 100755 spec/unit/reports_spec_spec.rb create mode 100755 spec/unit/resource/catalog_spec.rb delete mode 100755 spec/unit/resource/catalog_spec_spec.rb create mode 100755 spec/unit/resource/status_spec.rb delete mode 100755 spec/unit/resource/status_spec_spec.rb create mode 100644 spec/unit/resource/type_collection_helper_spec.rb delete mode 100644 spec/unit/resource/type_collection_helper_spec_spec.rb create mode 100644 spec/unit/resource/type_collection_spec.rb delete mode 100644 spec/unit/resource/type_collection_spec_spec.rb create mode 100755 spec/unit/resource/type_spec.rb delete mode 100755 spec/unit/resource/type_spec_spec.rb create mode 100755 spec/unit/resource_spec.rb delete mode 100755 spec/unit/resource_spec_spec.rb create mode 100755 spec/unit/run_spec.rb delete mode 100755 spec/unit/run_spec_spec.rb create mode 100755 spec/unit/simple_graph_spec.rb delete mode 100755 spec/unit/simple_graph_spec_spec.rb create mode 100755 spec/unit/ssl/base_spec.rb delete mode 100755 spec/unit/ssl/base_spec_spec.rb create mode 100755 spec/unit/ssl/certificate_authority/interface_spec.rb delete mode 100755 spec/unit/ssl/certificate_authority/interface_spec_spec.rb create mode 100755 spec/unit/ssl/certificate_authority_spec.rb delete mode 100755 spec/unit/ssl/certificate_authority_spec_spec.rb create mode 100755 spec/unit/ssl/certificate_factory_spec.rb delete mode 100755 spec/unit/ssl/certificate_factory_spec_spec.rb create mode 100755 spec/unit/ssl/certificate_request_spec.rb delete mode 100755 spec/unit/ssl/certificate_request_spec_spec.rb create mode 100755 spec/unit/ssl/certificate_revocation_list_spec.rb delete mode 100755 spec/unit/ssl/certificate_revocation_list_spec_spec.rb create mode 100755 spec/unit/ssl/certificate_spec.rb delete mode 100755 spec/unit/ssl/certificate_spec_spec.rb create mode 100755 spec/unit/ssl/host_spec.rb delete mode 100755 spec/unit/ssl/host_spec_spec.rb create mode 100755 spec/unit/ssl/inventory_spec.rb delete mode 100755 spec/unit/ssl/inventory_spec_spec.rb create mode 100755 spec/unit/ssl/key_spec.rb delete mode 100755 spec/unit/ssl/key_spec_spec.rb create mode 100644 spec/unit/status_spec.rb delete mode 100644 spec/unit/status_spec_spec.rb create mode 100755 spec/unit/transaction/change_spec.rb delete mode 100755 spec/unit/transaction/change_spec_spec.rb create mode 100755 spec/unit/transaction/event_manager_spec.rb delete mode 100755 spec/unit/transaction/event_manager_spec_spec.rb create mode 100755 spec/unit/transaction/event_spec.rb delete mode 100755 spec/unit/transaction/event_spec_spec.rb create mode 100755 spec/unit/transaction/report_spec.rb delete mode 100755 spec/unit/transaction/report_spec_spec.rb create mode 100755 spec/unit/transaction/resource_harness_spec.rb delete mode 100755 spec/unit/transaction/resource_harness_spec_spec.rb create mode 100755 spec/unit/transaction_spec.rb delete mode 100755 spec/unit/transaction_spec_spec.rb create mode 100644 spec/unit/transportable_spec.rb delete mode 100644 spec/unit/transportable_spec_spec.rb create mode 100644 spec/unit/type/augeas_spec.rb delete mode 100644 spec/unit/type/augeas_spec_spec.rb create mode 100755 spec/unit/type/component_spec.rb delete mode 100755 spec/unit/type/component_spec_spec.rb create mode 100755 spec/unit/type/computer_spec.rb delete mode 100755 spec/unit/type/computer_spec_spec.rb create mode 100755 spec/unit/type/cron_spec.rb delete mode 100755 spec/unit/type/cron_spec_spec.rb create mode 100755 spec/unit/type/exec_spec.rb delete mode 100755 spec/unit/type/exec_spec_spec.rb create mode 100644 spec/unit/type/file/checksum_spec.rb delete mode 100644 spec/unit/type/file/checksum_spec_spec.rb create mode 100755 spec/unit/type/file/content_spec.rb delete mode 100755 spec/unit/type/file/content_spec_spec.rb create mode 100755 spec/unit/type/file/ensure_spec.rb delete mode 100755 spec/unit/type/file/ensure_spec_spec.rb create mode 100755 spec/unit/type/file/group_spec.rb delete mode 100755 spec/unit/type/file/group_spec_spec.rb create mode 100755 spec/unit/type/file/owner_spec.rb delete mode 100755 spec/unit/type/file/owner_spec_spec.rb create mode 100644 spec/unit/type/file/selinux_spec.rb delete mode 100644 spec/unit/type/file/selinux_spec_spec.rb create mode 100755 spec/unit/type/file/source_spec.rb delete mode 100755 spec/unit/type/file/source_spec_spec.rb create mode 100755 spec/unit/type/file_spec.rb delete mode 100755 spec/unit/type/file_spec_spec.rb create mode 100644 spec/unit/type/filebucket_spec.rb delete mode 100644 spec/unit/type/filebucket_spec_spec.rb create mode 100755 spec/unit/type/group_spec.rb delete mode 100755 spec/unit/type/group_spec_spec.rb create mode 100755 spec/unit/type/macauthorization_spec.rb delete mode 100755 spec/unit/type/macauthorization_spec_spec.rb create mode 100755 spec/unit/type/maillist_spec.rb delete mode 100755 spec/unit/type/maillist_spec_spec.rb create mode 100755 spec/unit/type/mcx_spec.rb delete mode 100755 spec/unit/type/mcx_spec_spec.rb create mode 100755 spec/unit/type/mount_spec.rb delete mode 100755 spec/unit/type/mount_spec_spec.rb create mode 100755 spec/unit/type/nagios_spec.rb delete mode 100755 spec/unit/type/nagios_spec_spec.rb create mode 100755 spec/unit/type/noop_metaparam_spec.rb delete mode 100755 spec/unit/type/noop_metaparam_spec_spec.rb create mode 100755 spec/unit/type/package_spec.rb delete mode 100755 spec/unit/type/package_spec_spec.rb create mode 100644 spec/unit/type/resources_spec.rb delete mode 100644 spec/unit/type/resources_spec_spec.rb create mode 100755 spec/unit/type/schedule_spec.rb delete mode 100755 spec/unit/type/schedule_spec_spec.rb create mode 100755 spec/unit/type/selboolean_spec.rb delete mode 100755 spec/unit/type/selboolean_spec_spec.rb create mode 100755 spec/unit/type/selmodule_spec.rb delete mode 100755 spec/unit/type/selmodule_spec_spec.rb create mode 100755 spec/unit/type/service_spec.rb delete mode 100755 spec/unit/type/service_spec_spec.rb create mode 100755 spec/unit/type/ssh_authorized_key_spec.rb delete mode 100755 spec/unit/type/ssh_authorized_key_spec_spec.rb create mode 100644 spec/unit/type/stage_spec.rb delete mode 100644 spec/unit/type/stage_spec_spec.rb create mode 100755 spec/unit/type/tidy_spec.rb delete mode 100755 spec/unit/type/tidy_spec_spec.rb create mode 100755 spec/unit/type/user_spec.rb delete mode 100755 spec/unit/type/user_spec_spec.rb create mode 100755 spec/unit/type/zfs_spec.rb delete mode 100755 spec/unit/type/zfs_spec_spec.rb create mode 100755 spec/unit/type/zone_spec.rb delete mode 100755 spec/unit/type/zone_spec_spec.rb create mode 100755 spec/unit/type/zpool_spec.rb delete mode 100755 spec/unit/type/zpool_spec_spec.rb create mode 100755 spec/unit/type_spec.rb delete mode 100755 spec/unit/type_spec_spec.rb create mode 100755 spec/unit/util/autoload/file_cache_spec.rb delete mode 100755 spec/unit/util/autoload/file_cache_spec_spec.rb create mode 100755 spec/unit/util/autoload_spec.rb delete mode 100755 spec/unit/util/autoload_spec_spec.rb create mode 100755 spec/unit/util/backups_spec.rb delete mode 100755 spec/unit/util/backups_spec_spec.rb create mode 100644 spec/unit/util/cache_accumulator_spec.rb delete mode 100644 spec/unit/util/cache_accumulator_spec_spec.rb create mode 100755 spec/unit/util/cacher_spec.rb delete mode 100755 spec/unit/util/cacher_spec_spec.rb create mode 100755 spec/unit/util/checksums_spec.rb delete mode 100755 spec/unit/util/checksums_spec_spec.rb create mode 100644 spec/unit/util/command_line_spec.rb delete mode 100644 spec/unit/util/command_line_spec_spec.rb create mode 100755 spec/unit/util/constant_inflector_spec.rb delete mode 100755 spec/unit/util/constant_inflector_spec_spec.rb create mode 100755 spec/unit/util/errors_spec.rb delete mode 100755 spec/unit/util/errors_spec_spec.rb create mode 100755 spec/unit/util/feature_spec.rb delete mode 100755 spec/unit/util/feature_spec_spec.rb create mode 100755 spec/unit/util/file_locking_spec.rb delete mode 100755 spec/unit/util/file_locking_spec_spec.rb create mode 100644 spec/unit/util/filetype_spec.rb delete mode 100644 spec/unit/util/filetype_spec_spec.rb create mode 100755 spec/unit/util/inline_docs_spec.rb delete mode 100755 spec/unit/util/inline_docs_spec_spec.rb create mode 100755 spec/unit/util/json_spec.rb delete mode 100755 spec/unit/util/json_spec_spec.rb create mode 100755 spec/unit/util/ldap/connection_spec.rb delete mode 100755 spec/unit/util/ldap/connection_spec_spec.rb create mode 100755 spec/unit/util/ldap/generator_spec.rb delete mode 100755 spec/unit/util/ldap/generator_spec_spec.rb create mode 100755 spec/unit/util/ldap/manager_spec.rb delete mode 100755 spec/unit/util/ldap/manager_spec_spec.rb create mode 100755 spec/unit/util/loadedfile_spec.rb delete mode 100755 spec/unit/util/loadedfile_spec_spec.rb create mode 100755 spec/unit/util/log/destinations_spec.rb delete mode 100755 spec/unit/util/log/destinations_spec_spec.rb create mode 100755 spec/unit/util/log_spec.rb delete mode 100755 spec/unit/util/log_spec_spec.rb create mode 100755 spec/unit/util/logging_spec.rb delete mode 100755 spec/unit/util/logging_spec_spec.rb create mode 100755 spec/unit/util/metric_spec.rb delete mode 100755 spec/unit/util/metric_spec_spec.rb create mode 100644 spec/unit/util/monkey_patches_spec.rb delete mode 100644 spec/unit/util/monkey_patches_spec_spec.rb create mode 100755 spec/unit/util/nagios_maker_spec.rb delete mode 100755 spec/unit/util/nagios_maker_spec_spec.rb create mode 100644 spec/unit/util/package_spec.rb delete mode 100644 spec/unit/util/package_spec_spec.rb create mode 100755 spec/unit/util/posix_spec.rb delete mode 100755 spec/unit/util/posix_spec_spec.rb create mode 100755 spec/unit/util/queue/stomp_spec.rb delete mode 100755 spec/unit/util/queue/stomp_spec_spec.rb create mode 100755 spec/unit/util/queue_spec.rb delete mode 100755 spec/unit/util/queue_spec_spec.rb create mode 100755 spec/unit/util/rdoc/parser_spec.rb delete mode 100755 spec/unit/util/rdoc/parser_spec_spec.rb create mode 100755 spec/unit/util/rdoc_spec.rb delete mode 100755 spec/unit/util/rdoc_spec_spec.rb create mode 100644 spec/unit/util/reference_serializer_spec.rb delete mode 100644 spec/unit/util/reference_serializer_spec_spec.rb create mode 100755 spec/unit/util/resource_template_spec.rb delete mode 100755 spec/unit/util/resource_template_spec_spec.rb create mode 100755 spec/unit/util/selinux_spec.rb delete mode 100755 spec/unit/util/selinux_spec_spec.rb create mode 100755 spec/unit/util/settings/file_setting_spec.rb delete mode 100755 spec/unit/util/settings/file_setting_spec_spec.rb create mode 100755 spec/unit/util/settings_spec.rb delete mode 100755 spec/unit/util/settings_spec_spec.rb create mode 100755 spec/unit/util/storage_spec.rb delete mode 100755 spec/unit/util/storage_spec_spec.rb create mode 100755 spec/unit/util/tagging_spec.rb delete mode 100755 spec/unit/util/tagging_spec_spec.rb create mode 100644 spec/unit/util/user_attr_spec.rb delete mode 100644 spec/unit/util/user_attr_spec_spec.rb create mode 100755 spec/unit/util/warnings_spec.rb delete mode 100755 spec/unit/util/warnings_spec_spec.rb create mode 100644 spec/unit/util/zaml_spec.rb delete mode 100644 spec/unit/util/zaml_spec_spec.rb diff --git a/spec/unit/agent/locker_spec.rb b/spec/unit/agent/locker_spec.rb new file mode 100755 index 000000000..1477c824e --- /dev/null +++ b/spec/unit/agent/locker_spec.rb @@ -0,0 +1,100 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/agent' +require 'puppet/agent/locker' + +class LockerTester + include Puppet::Agent::Locker +end + +describe Puppet::Agent::Locker do + before do + @locker = LockerTester.new + @locker.stubs(:lockfile_path).returns "/my/lock" + end + + it "should use a Pidlock instance as its lockfile" do + @locker.lockfile.should be_instance_of(Puppet::Util::Pidlock) + end + + it "should use 'lockfile_path' to determine its lockfile path" do + @locker.expects(:lockfile_path).returns "/my/lock" + lock = Puppet::Util::Pidlock.new("/my/lock") + Puppet::Util::Pidlock.expects(:new).with("/my/lock").returns lock + + @locker.lockfile + end + + it "should reuse the same lock file each time" do + @locker.lockfile.should equal(@locker.lockfile) + end + + it "should use the lock file to anonymously lock the process when disabled" do + @locker.lockfile.expects(:lock).with(:anonymous => true) + + @locker.disable + end + + it "should use the lock file to anonymously unlock the process when enabled" do + @locker.lockfile.expects(:unlock).with(:anonymous => true) + + @locker.enable + end + + it "should have a method that yields when a lock is attained" do + @locker.lockfile.expects(:lock).returns true + + yielded = false + @locker.lock do + yielded = true + end + yielded.should be_true + end + + it "should return true when the lock method successfully locked" do + @locker.lockfile.expects(:lock).returns true + + @locker.lock {}.should be_true + end + + it "should return true when the lock method does not receive the lock" do + @locker.lockfile.expects(:lock).returns false + + @locker.lock {}.should be_false + end + + it "should not yield when the lock method does not receive the lock" do + @locker.lockfile.expects(:lock).returns false + + yielded = false + @locker.lock { yielded = true } + yielded.should be_false + end + + it "should not unlock when a lock was not received" do + @locker.lockfile.expects(:lock).returns false + @locker.lockfile.expects(:unlock).never + + @locker.lock {} + end + + it "should unlock after yielding upon obtaining a lock" do + @locker.lockfile.stubs(:lock).returns true + @locker.lockfile.expects(:unlock) + + @locker.lock {} + end + + it "should unlock after yielding upon obtaining a lock, even if the block throws an exception" do + @locker.lockfile.stubs(:lock).returns true + @locker.lockfile.expects(:unlock) + + lambda { @locker.lock { raise "foo" } }.should raise_error(RuntimeError) + end + + it "should be considered running if the lockfile is locked" do + @locker.lockfile.expects(:locked?).returns true + @locker.should be_running + end +end diff --git a/spec/unit/agent/locker_spec_spec.rb b/spec/unit/agent/locker_spec_spec.rb deleted file mode 100755 index 1477c824e..000000000 --- a/spec/unit/agent/locker_spec_spec.rb +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/agent' -require 'puppet/agent/locker' - -class LockerTester - include Puppet::Agent::Locker -end - -describe Puppet::Agent::Locker do - before do - @locker = LockerTester.new - @locker.stubs(:lockfile_path).returns "/my/lock" - end - - it "should use a Pidlock instance as its lockfile" do - @locker.lockfile.should be_instance_of(Puppet::Util::Pidlock) - end - - it "should use 'lockfile_path' to determine its lockfile path" do - @locker.expects(:lockfile_path).returns "/my/lock" - lock = Puppet::Util::Pidlock.new("/my/lock") - Puppet::Util::Pidlock.expects(:new).with("/my/lock").returns lock - - @locker.lockfile - end - - it "should reuse the same lock file each time" do - @locker.lockfile.should equal(@locker.lockfile) - end - - it "should use the lock file to anonymously lock the process when disabled" do - @locker.lockfile.expects(:lock).with(:anonymous => true) - - @locker.disable - end - - it "should use the lock file to anonymously unlock the process when enabled" do - @locker.lockfile.expects(:unlock).with(:anonymous => true) - - @locker.enable - end - - it "should have a method that yields when a lock is attained" do - @locker.lockfile.expects(:lock).returns true - - yielded = false - @locker.lock do - yielded = true - end - yielded.should be_true - end - - it "should return true when the lock method successfully locked" do - @locker.lockfile.expects(:lock).returns true - - @locker.lock {}.should be_true - end - - it "should return true when the lock method does not receive the lock" do - @locker.lockfile.expects(:lock).returns false - - @locker.lock {}.should be_false - end - - it "should not yield when the lock method does not receive the lock" do - @locker.lockfile.expects(:lock).returns false - - yielded = false - @locker.lock { yielded = true } - yielded.should be_false - end - - it "should not unlock when a lock was not received" do - @locker.lockfile.expects(:lock).returns false - @locker.lockfile.expects(:unlock).never - - @locker.lock {} - end - - it "should unlock after yielding upon obtaining a lock" do - @locker.lockfile.stubs(:lock).returns true - @locker.lockfile.expects(:unlock) - - @locker.lock {} - end - - it "should unlock after yielding upon obtaining a lock, even if the block throws an exception" do - @locker.lockfile.stubs(:lock).returns true - @locker.lockfile.expects(:unlock) - - lambda { @locker.lock { raise "foo" } }.should raise_error(RuntimeError) - end - - it "should be considered running if the lockfile is locked" do - @locker.lockfile.expects(:locked?).returns true - @locker.should be_running - end -end diff --git a/spec/unit/agent_spec.rb b/spec/unit/agent_spec.rb new file mode 100755 index 000000000..0a7fd5715 --- /dev/null +++ b/spec/unit/agent_spec.rb @@ -0,0 +1,281 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-11-12. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../spec_helper' +require 'puppet/agent' + +class AgentTestClient + def run + # no-op + end + def stop + # no-op + end +end + +def without_warnings + flag = $VERBOSE + $VERBOSE = nil + yield + $VERBOSE = flag +end + +describe Puppet::Agent do + before do + @agent = Puppet::Agent.new(AgentTestClient) + + # So we don't actually try to hit the filesystem. + @agent.stubs(:lock).yields + + # make Puppet::Application safe for stubbing; restore in an :after block; silence warnings for this. + without_warnings { Puppet::Application = Class.new(Puppet::Application) } + Puppet::Application.stubs(:clear?).returns(true) + Puppet::Application.class_eval do + class << self + def controlled_run(&block) + block.call + end + end + end + end + + after do + # restore Puppet::Application from stub-safe subclass, and silence warnings + without_warnings { Puppet::Application = Puppet::Application.superclass } + end + + it "should set its client class at initialization" do + Puppet::Agent.new("foo").client_class.should == "foo" + end + + it "should include the Locker module" do + Puppet::Agent.ancestors.should be_include(Puppet::Agent::Locker) + end + + it "should create an instance of its client class and run it when asked to run" do + client = mock 'client' + AgentTestClient.expects(:new).returns client + + client.expects(:run) + + @agent.stubs(:running?).returns false + @agent.run + end + + it "should determine its lock file path by asking the client class" do + AgentTestClient.expects(:lockfile_path).returns "/my/lock" + @agent.lockfile_path.should == "/my/lock" + end + + it "should be considered running if the lock file is locked" do + lockfile = mock 'lockfile' + + @agent.expects(:lockfile).returns lockfile + lockfile.expects(:locked?).returns true + + @agent.should be_running + end + + describe "when being run" do + before do + @agent.stubs(:running?).returns false + end + + it "should splay" do + @agent.expects(:splay) + @agent.stubs(:running?).returns false + + @agent.run + end + + it "should do nothing if already running" do + @agent.expects(:running?).returns true + AgentTestClient.expects(:new).never + @agent.run + end + + it "should use Puppet::Application.controlled_run to manage process state behavior" do + calls = sequence('calls') + Puppet::Application.expects(:controlled_run).yields().in_sequence(calls) + AgentTestClient.expects(:new).once.in_sequence(calls) + @agent.run + end + + it "should not fail if a client class instance cannot be created" do + AgentTestClient.expects(:new).raises "eh" + Puppet.expects(:err) + @agent.run + end + + it "should not fail if there is an exception while running its client" do + client = AgentTestClient.new + AgentTestClient.expects(:new).returns client + client.expects(:run).raises "eh" + Puppet.expects(:err) + @agent.run + end + + it "should use a mutex to restrict multi-threading" do + client = AgentTestClient.new + AgentTestClient.expects(:new).returns client + + mutex = mock 'mutex' + @agent.expects(:sync).returns mutex + + mutex.expects(:synchronize) + client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it + @agent.run + end + + it "should use a filesystem lock to restrict multiple processes running the agent" do + client = AgentTestClient.new + AgentTestClient.expects(:new).returns client + + @agent.expects(:lock) + + client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it + @agent.run + end + + it "should make its client instance available while running" do + client = AgentTestClient.new + AgentTestClient.expects(:new).returns client + + client.expects(:run).with { @agent.client.should equal(client); true } + @agent.run + end + + it "should run the client instance with any arguments passed to it" do + client = AgentTestClient.new + AgentTestClient.expects(:new).returns client + + client.expects(:run).with("testargs") + @agent.run("testargs") + end + end + + describe "when splaying" do + before do + Puppet.settings.stubs(:value).with(:splay).returns true + Puppet.settings.stubs(:value).with(:splaylimit).returns "10" + end + + it "should do nothing if splay is disabled" do + Puppet.settings.expects(:value).returns false + @agent.expects(:sleep).never + @agent.splay + end + + it "should do nothing if it has already splayed" do + @agent.expects(:splayed?).returns true + @agent.expects(:sleep).never + @agent.splay + end + + it "should log that it is splaying" do + @agent.stubs :sleep + Puppet.expects :info + @agent.splay + end + + it "should sleep for a random portion of the splaylimit plus 1" do + Puppet.settings.expects(:value).with(:splaylimit).returns "50" + @agent.expects(:rand).with(51).returns 10 + @agent.expects(:sleep).with(10) + @agent.splay + end + + it "should mark that it has splayed" do + @agent.stubs(:sleep) + @agent.splay + @agent.should be_splayed + end + end + + describe "when checking execution state" do + describe 'with regular run status' do + before :each do + Puppet::Application.stubs(:restart_requested?).returns(false) + Puppet::Application.stubs(:stop_requested?).returns(false) + Puppet::Application.stubs(:interrupted?).returns(false) + Puppet::Application.stubs(:clear?).returns(true) + end + + it 'should be false for :stopping?' do + @agent.stopping?.should be_false + end + + it 'should be false for :needing_restart?' do + @agent.needing_restart?.should be_false + end + end + + describe 'with a stop requested' do + before :each do + Puppet::Application.stubs(:clear?).returns(false) + Puppet::Application.stubs(:restart_requested?).returns(false) + Puppet::Application.stubs(:stop_requested?).returns(true) + Puppet::Application.stubs(:interrupted?).returns(true) + end + + it 'should be true for :stopping?' do + @agent.stopping?.should be_true + end + + it 'should be false for :needing_restart?' do + @agent.needing_restart?.should be_false + end + end + + describe 'with a restart requested' do + before :each do + Puppet::Application.stubs(:clear?).returns(false) + Puppet::Application.stubs(:restart_requested?).returns(true) + Puppet::Application.stubs(:stop_requested?).returns(false) + Puppet::Application.stubs(:interrupted?).returns(true) + end + + it 'should be false for :stopping?' do + @agent.stopping?.should be_false + end + + it 'should be true for :needing_restart?' do + @agent.needing_restart?.should be_true + end + end + end + + describe "when starting" do + before do + @agent.stubs(:observe_signal) + end + + it "should create a timer with the runinterval, a tolerance of 1, and :start? set to true" do + Puppet.settings.expects(:value).with(:runinterval).returns 5 + timer = stub 'timer', :sound_alarm => nil + EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1).returns timer + + @agent.stubs(:run) + @agent.start + end + + it "should run once immediately" do + timer = mock 'timer' + EventLoop::Timer.expects(:new).returns timer + + timer.expects(:sound_alarm) + + @agent.start + end + + it "should run within the block passed to the timer" do + timer = stub 'timer', :sound_alarm => nil + EventLoop::Timer.expects(:new).returns(timer).yields + @agent.expects(:run) + + @agent.start + end + end +end diff --git a/spec/unit/agent_spec_spec.rb b/spec/unit/agent_spec_spec.rb deleted file mode 100755 index 0a7fd5715..000000000 --- a/spec/unit/agent_spec_spec.rb +++ /dev/null @@ -1,281 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-11-12. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../spec_helper' -require 'puppet/agent' - -class AgentTestClient - def run - # no-op - end - def stop - # no-op - end -end - -def without_warnings - flag = $VERBOSE - $VERBOSE = nil - yield - $VERBOSE = flag -end - -describe Puppet::Agent do - before do - @agent = Puppet::Agent.new(AgentTestClient) - - # So we don't actually try to hit the filesystem. - @agent.stubs(:lock).yields - - # make Puppet::Application safe for stubbing; restore in an :after block; silence warnings for this. - without_warnings { Puppet::Application = Class.new(Puppet::Application) } - Puppet::Application.stubs(:clear?).returns(true) - Puppet::Application.class_eval do - class << self - def controlled_run(&block) - block.call - end - end - end - end - - after do - # restore Puppet::Application from stub-safe subclass, and silence warnings - without_warnings { Puppet::Application = Puppet::Application.superclass } - end - - it "should set its client class at initialization" do - Puppet::Agent.new("foo").client_class.should == "foo" - end - - it "should include the Locker module" do - Puppet::Agent.ancestors.should be_include(Puppet::Agent::Locker) - end - - it "should create an instance of its client class and run it when asked to run" do - client = mock 'client' - AgentTestClient.expects(:new).returns client - - client.expects(:run) - - @agent.stubs(:running?).returns false - @agent.run - end - - it "should determine its lock file path by asking the client class" do - AgentTestClient.expects(:lockfile_path).returns "/my/lock" - @agent.lockfile_path.should == "/my/lock" - end - - it "should be considered running if the lock file is locked" do - lockfile = mock 'lockfile' - - @agent.expects(:lockfile).returns lockfile - lockfile.expects(:locked?).returns true - - @agent.should be_running - end - - describe "when being run" do - before do - @agent.stubs(:running?).returns false - end - - it "should splay" do - @agent.expects(:splay) - @agent.stubs(:running?).returns false - - @agent.run - end - - it "should do nothing if already running" do - @agent.expects(:running?).returns true - AgentTestClient.expects(:new).never - @agent.run - end - - it "should use Puppet::Application.controlled_run to manage process state behavior" do - calls = sequence('calls') - Puppet::Application.expects(:controlled_run).yields().in_sequence(calls) - AgentTestClient.expects(:new).once.in_sequence(calls) - @agent.run - end - - it "should not fail if a client class instance cannot be created" do - AgentTestClient.expects(:new).raises "eh" - Puppet.expects(:err) - @agent.run - end - - it "should not fail if there is an exception while running its client" do - client = AgentTestClient.new - AgentTestClient.expects(:new).returns client - client.expects(:run).raises "eh" - Puppet.expects(:err) - @agent.run - end - - it "should use a mutex to restrict multi-threading" do - client = AgentTestClient.new - AgentTestClient.expects(:new).returns client - - mutex = mock 'mutex' - @agent.expects(:sync).returns mutex - - mutex.expects(:synchronize) - client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it - @agent.run - end - - it "should use a filesystem lock to restrict multiple processes running the agent" do - client = AgentTestClient.new - AgentTestClient.expects(:new).returns client - - @agent.expects(:lock) - - client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it - @agent.run - end - - it "should make its client instance available while running" do - client = AgentTestClient.new - AgentTestClient.expects(:new).returns client - - client.expects(:run).with { @agent.client.should equal(client); true } - @agent.run - end - - it "should run the client instance with any arguments passed to it" do - client = AgentTestClient.new - AgentTestClient.expects(:new).returns client - - client.expects(:run).with("testargs") - @agent.run("testargs") - end - end - - describe "when splaying" do - before do - Puppet.settings.stubs(:value).with(:splay).returns true - Puppet.settings.stubs(:value).with(:splaylimit).returns "10" - end - - it "should do nothing if splay is disabled" do - Puppet.settings.expects(:value).returns false - @agent.expects(:sleep).never - @agent.splay - end - - it "should do nothing if it has already splayed" do - @agent.expects(:splayed?).returns true - @agent.expects(:sleep).never - @agent.splay - end - - it "should log that it is splaying" do - @agent.stubs :sleep - Puppet.expects :info - @agent.splay - end - - it "should sleep for a random portion of the splaylimit plus 1" do - Puppet.settings.expects(:value).with(:splaylimit).returns "50" - @agent.expects(:rand).with(51).returns 10 - @agent.expects(:sleep).with(10) - @agent.splay - end - - it "should mark that it has splayed" do - @agent.stubs(:sleep) - @agent.splay - @agent.should be_splayed - end - end - - describe "when checking execution state" do - describe 'with regular run status' do - before :each do - Puppet::Application.stubs(:restart_requested?).returns(false) - Puppet::Application.stubs(:stop_requested?).returns(false) - Puppet::Application.stubs(:interrupted?).returns(false) - Puppet::Application.stubs(:clear?).returns(true) - end - - it 'should be false for :stopping?' do - @agent.stopping?.should be_false - end - - it 'should be false for :needing_restart?' do - @agent.needing_restart?.should be_false - end - end - - describe 'with a stop requested' do - before :each do - Puppet::Application.stubs(:clear?).returns(false) - Puppet::Application.stubs(:restart_requested?).returns(false) - Puppet::Application.stubs(:stop_requested?).returns(true) - Puppet::Application.stubs(:interrupted?).returns(true) - end - - it 'should be true for :stopping?' do - @agent.stopping?.should be_true - end - - it 'should be false for :needing_restart?' do - @agent.needing_restart?.should be_false - end - end - - describe 'with a restart requested' do - before :each do - Puppet::Application.stubs(:clear?).returns(false) - Puppet::Application.stubs(:restart_requested?).returns(true) - Puppet::Application.stubs(:stop_requested?).returns(false) - Puppet::Application.stubs(:interrupted?).returns(true) - end - - it 'should be false for :stopping?' do - @agent.stopping?.should be_false - end - - it 'should be true for :needing_restart?' do - @agent.needing_restart?.should be_true - end - end - end - - describe "when starting" do - before do - @agent.stubs(:observe_signal) - end - - it "should create a timer with the runinterval, a tolerance of 1, and :start? set to true" do - Puppet.settings.expects(:value).with(:runinterval).returns 5 - timer = stub 'timer', :sound_alarm => nil - EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1).returns timer - - @agent.stubs(:run) - @agent.start - end - - it "should run once immediately" do - timer = mock 'timer' - EventLoop::Timer.expects(:new).returns timer - - timer.expects(:sound_alarm) - - @agent.start - end - - it "should run within the block passed to the timer" do - timer = stub 'timer', :sound_alarm => nil - EventLoop::Timer.expects(:new).returns(timer).yields - @agent.expects(:run) - - @agent.start - end - end -end diff --git a/spec/unit/application/agent_spec.rb b/spec/unit/application/agent_spec.rb new file mode 100755 index 000000000..76a378e82 --- /dev/null +++ b/spec/unit/application/agent_spec.rb @@ -0,0 +1,600 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/agent' +require 'puppet/application/agent' +require 'puppet/network/server' +require 'puppet/daemon' + +describe Puppet::Application::Agent do + before :each do + @puppetd = Puppet::Application[:agent] + @puppetd.stubs(:puts) + @daemon = stub_everything 'daemon' + Puppet::Daemon.stubs(:new).returns(@daemon) + @agent = stub_everything 'agent' + Puppet::Agent.stubs(:new).returns(@agent) + @puppetd.preinit + Puppet::Util::Log.stubs(:newdestination) + Puppet::Util::Log.stubs(:level=) + + Puppet::Node.stubs(:terminus_class=) + Puppet::Node.stubs(:cache_class=) + Puppet::Node::Facts.stubs(:terminus_class=) + end + + it "should ask Puppet::Application to parse Puppet configuration file" do + @puppetd.should_parse_config?.should be_true + end + + it "should declare a main command" do + @puppetd.should respond_to(:main) + end + + it "should declare a onetime command" do + @puppetd.should respond_to(:onetime) + end + + it "should declare a fingerprint command" do + @puppetd.should respond_to(:fingerprint) + end + + it "should declare a preinit block" do + @puppetd.should respond_to(:preinit) + end + + describe "in preinit" do + before :each do + @puppetd.stubs(:trap) + end + + it "should catch INT" do + @puppetd.expects(:trap).with { |arg,block| arg == :INT } + + @puppetd.preinit + end + + it "should set waitforcert to 120" do + @puppetd.preinit + + @puppetd.options[:waitforcert].should == 120 + end + + it "should init client to true" do + @puppetd.preinit + + @puppetd.options[:client].should be_true + end + + it "should init fqdn to nil" do + @puppetd.preinit + + @puppetd.options[:fqdn].should be_nil + end + + it "should init serve to []" do + @puppetd.preinit + + @puppetd.options[:serve].should == [] + end + + it "should use MD5 as default digest algorithm" do + @puppetd.preinit + + @puppetd.options[:digest].should == :MD5 + end + + it "should not fingerprint by default" do + @puppetd.preinit + + @puppetd.options[:fingerprint].should be_false + end + end + + describe "when handling options" do + before do + @puppetd.command_line.stubs(:args).returns([]) + end + + [:centrallogging, :disable, :enable, :debug, :fqdn, :test, :verbose, :digest].each do |option| + it "should declare handle_#{option} method" do + @puppetd.should respond_to("handle_#{option}".to_sym) + end + + it "should store argument value when calling handle_#{option}" do + @puppetd.options.expects(:[]=).with(option, 'arg') + @puppetd.send("handle_#{option}".to_sym, 'arg') + end + end + + it "should set an existing handler on server" do + Puppet::Network::Handler.stubs(:handler).with("handler").returns(true) + + @puppetd.handle_serve("handler") + @puppetd.options[:serve].should == [ :handler ] + end + + it "should set client to false with --no-client" do + @puppetd.handle_no_client(nil) + @puppetd.options[:client].should be_false + end + + it "should set onetime to true with --onetime" do + @puppetd.handle_onetime(nil) + Puppet[:onetime].should be_true + end + + it "should set waitforcert to 0 with --onetime and if --waitforcert wasn't given" do + @puppetd.explicit_waitforcert = false + @puppetd.handle_onetime(nil) + @puppetd.options[:waitforcert].should == 0 + end + + it "should not reset waitforcert with --onetime when --waitforcert is used" do + @puppetd.explicit_waitforcert = true + @puppetd.handle_onetime(nil) + @puppetd.options[:waitforcert].should_not == 0 + end + + it "should set the log destination with --logdest" do + @puppetd.options.stubs(:[]=).with { |opt,val| opt == :setdest } + Puppet::Log.expects(:newdestination).with("console") + + @puppetd.handle_logdest("console") + end + + it "should put the setdest options to true" do + @puppetd.options.expects(:[]=).with(:setdest,true) + + @puppetd.handle_logdest("console") + end + + it "should parse the log destination from the command line" do + @puppetd.command_line.stubs(:args).returns(%w[--logdest /my/file]) + + Puppet::Util::Log.expects(:newdestination).with("/my/file") + + @puppetd.parse_options + end + + it "should store the waitforcert options with --waitforcert" do + @puppetd.options.expects(:[]=).with(:waitforcert,42) + + @puppetd.handle_waitforcert("42") + end + + it "should mark explicit_waitforcert to true with --waitforcert" do + @puppetd.options.stubs(:[]=) + + @puppetd.handle_waitforcert("42") + @puppetd.explicit_waitforcert.should be_true + end + + it "should set args[:Port] with --port" do + @puppetd.handle_port("42") + @puppetd.args[:Port].should == "42" + end + + end + + describe "during setup" do + before :each do + @puppetd.options.stubs(:[]) + Puppet.stubs(:info) + FileTest.stubs(:exists?).returns(true) + Puppet.stubs(:[]) + Puppet.stubs(:[]=) + Puppet.stubs(:[]).with(:libdir).returns("/dev/null/lib") + Puppet.settings.stubs(:print_config?) + Puppet.settings.stubs(:print_config) + Puppet::SSL::Host.stubs(:ca_location=) + Puppet::Transaction::Report.stubs(:terminus_class=) + Puppet::Resource::Catalog.stubs(:terminus_class=) + Puppet::Resource::Catalog.stubs(:cache_class=) + Puppet::Node::Facts.stubs(:terminus_class=) + @host = stub_everything 'host' + Puppet::SSL::Host.stubs(:new).returns(@host) + Puppet.stubs(:settraps) + end + + describe "with --test" do + before :each do + Puppet.settings.stubs(:handlearg) + @puppetd.options.stubs(:[]=) + end + + it "should call setup_test" do + @puppetd.options.stubs(:[]).with(:test).returns(true) + @puppetd.expects(:setup_test) + @puppetd.setup + end + + it "should set options[:verbose] to true" do + @puppetd.options.expects(:[]=).with(:verbose,true) + @puppetd.setup_test + end + it "should set options[:onetime] to true" do + Puppet.expects(:[]=).with(:onetime,true) + @puppetd.setup_test + end + it "should set options[:detailed_exitcodes] to true" do + @puppetd.options.expects(:[]=).with(:detailed_exitcodes,true) + @puppetd.setup_test + end + it "should set waitforcert to 0" do + @puppetd.options.expects(:[]=).with(:waitforcert,0) + @puppetd.setup_test + end + end + + it "should call setup_logs" do + @puppetd.expects(:setup_logs) + @puppetd.setup + end + + describe "when setting up logs" do + before :each do + Puppet::Util::Log.stubs(:newdestination) + end + + it "should set log level to debug if --debug was passed" do + @puppetd.options.stubs(:[]).with(:debug).returns(true) + + Puppet::Util::Log.expects(:level=).with(:debug) + + @puppetd.setup_logs + end + + it "should set log level to info if --verbose was passed" do + @puppetd.options.stubs(:[]).with(:verbose).returns(true) + + Puppet::Util::Log.expects(:level=).with(:info) + + @puppetd.setup_logs + end + + [:verbose, :debug].each do |level| + it "should set console as the log destination with level #{level}" do + @puppetd.options.stubs(:[]).with(level).returns(true) + + Puppet::Util::Log.expects(:newdestination).with(:console) + + @puppetd.setup_logs + end + end + + it "should set syslog as the log destination if no --logdest" do + @puppetd.options.stubs(:[]).with(:setdest).returns(false) + + Puppet::Util::Log.expects(:newdestination).with(:syslog) + + @puppetd.setup_logs + end + + end + + it "should print puppet config if asked to in Puppet config" do + @puppetd.stubs(:exit) + Puppet.settings.stubs(:print_configs?).returns(true) + + Puppet.settings.expects(:print_configs) + + @puppetd.setup + end + + it "should exit after printing puppet config if asked to in Puppet config" do + Puppet.settings.stubs(:print_configs?).returns(true) + + lambda { @puppetd.setup }.should raise_error(SystemExit) + end + + it "should set a central log destination with --centrallogs" do + @puppetd.options.stubs(:[]).with(:centrallogs).returns(true) + Puppet.stubs(:[]).with(:server).returns("puppet.reductivelabs.com") + Puppet::Util::Log.stubs(:newdestination).with(:syslog) + + Puppet::Util::Log.expects(:newdestination).with("puppet.reductivelabs.com") + + @puppetd.setup + end + + it "should use :main, :puppetd, and :ssl" do + Puppet.settings.expects(:use).with(:main, :agent, :ssl) + + @puppetd.setup + end + + it "should install a remote ca location" do + Puppet::SSL::Host.expects(:ca_location=).with(:remote) + + @puppetd.setup + end + + it "should install a none ca location in fingerprint mode" do + @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) + Puppet::SSL::Host.expects(:ca_location=).with(:none) + + @puppetd.setup + end + + it "should tell the report handler to use REST" do + Puppet::Transaction::Report.expects(:terminus_class=).with(:rest) + + @puppetd.setup + end + + it "should change the catalog_terminus setting to 'rest'" do + Puppet.expects(:[]=).with(:catalog_terminus, :rest) + @puppetd.setup + end + + it "should tell the catalog handler to use cache" do + Puppet::Resource::Catalog.expects(:cache_class=).with(:yaml) + + @puppetd.setup + end + + it "should change the facts_terminus setting to 'facter'" do + Puppet.expects(:[]=).with(:facts_terminus, :facter) + + @puppetd.setup + end + + it "should create an agent" do + Puppet::Agent.stubs(:new).with(Puppet::Configurer) + + @puppetd.setup + end + + [:enable, :disable].each do |action| + it "should delegate to enable_disable_client if we #{action} the agent" do + @puppetd.options.stubs(:[]).with(action).returns(true) + @puppetd.expects(:enable_disable_client).with(@agent) + + @puppetd.setup + end + end + + describe "when enabling or disabling agent" do + [:enable, :disable].each do |action| + it "should call client.#{action}" do + @puppetd.stubs(:exit) + @puppetd.options.stubs(:[]).with(action).returns(true) + + @agent.expects(action) + + @puppetd.enable_disable_client(@agent) + end + end + + it "should finally exit" do + lambda { @puppetd.enable_disable_client(@agent) }.should raise_error(SystemExit) + end + end + + it "should inform the daemon about our agent if :client is set to 'true'" do + @puppetd.options.expects(:[]).with(:client).returns true + @daemon.expects(:agent=).with(@agent) + @puppetd.setup + end + + it "should not inform the daemon about our agent if :client is set to 'false'" do + @puppetd.options[:client] = false + @daemon.expects(:agent=).never + @puppetd.setup + end + + it "should daemonize if needed" do + Puppet.stubs(:[]).with(:daemonize).returns(true) + + @daemon.expects(:daemonize) + + @puppetd.setup + end + + it "should wait for a certificate" do + @puppetd.options.stubs(:[]).with(:waitforcert).returns(123) + @host.expects(:wait_for_cert).with(123) + + @puppetd.setup + end + + it "should not wait for a certificate in fingerprint mode" do + @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) + @puppetd.options.stubs(:[]).with(:waitforcert).returns(123) + @host.expects(:wait_for_cert).never + + @puppetd.setup + end + + it "should setup listen if told to and not onetime" do + Puppet.stubs(:[]).with(:listen).returns(true) + @puppetd.options.stubs(:[]).with(:onetime).returns(false) + + @puppetd.expects(:setup_listen) + + @puppetd.setup + end + + describe "when setting up listen" do + before :each do + Puppet.stubs(:[]).with(:authconfig).returns('auth') + FileTest.stubs(:exists?).with('auth').returns(true) + File.stubs(:exist?).returns(true) + @puppetd.options.stubs(:[]).with(:serve).returns([]) + @puppetd.stubs(:exit) + @server = stub_everything 'server' + Puppet::Network::Server.stubs(:new).returns(@server) + end + + + it "should exit if no authorization file" do + Puppet.stubs(:err) + FileTest.stubs(:exists?).with('auth').returns(false) + + @puppetd.expects(:exit) + + @puppetd.setup_listen + end + + it "should create a server to listen on at least the Runner handler" do + Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Runner] } + + @puppetd.setup_listen + end + + it "should create a server to listen for specific handlers" do + @puppetd.options.stubs(:[]).with(:serve).returns([:handler]) + Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:handler] } + + @puppetd.setup_listen + end + + it "should use puppet default port" do + Puppet.stubs(:[]).with(:puppetport).returns(:port) + + Puppet::Network::Server.expects(:new).with { |args| args[:port] == :port } + + @puppetd.setup_listen + end + end + end + + + describe "when running" do + before :each do + @puppetd.agent = @agent + @puppetd.daemon = @daemon + @puppetd.options.stubs(:[]).with(:fingerprint).returns(false) + end + + it "should dispatch to fingerprint if --fingerprint is used" do + @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) + + @puppetd.stubs(:fingerprint) + @puppetd.run_command + end + + it "should dispatch to onetime if --onetime is used" do + @puppetd.options.stubs(:[]).with(:onetime).returns(true) + + @puppetd.stubs(:onetime) + @puppetd.run_command + end + + it "should dispatch to main if --onetime and --fingerprint are not used" do + @puppetd.options.stubs(:[]).with(:onetime).returns(false) + + @puppetd.stubs(:main) + @puppetd.run_command + end + + describe "with --onetime" do + + before :each do + @agent.stubs(:run).returns(:report) + @puppetd.options.stubs(:[]).with(:client).returns(:client) + @puppetd.options.stubs(:[]).with(:detailed_exitcodes).returns(false) + @puppetd.stubs(:exit).with(0) + Puppet.stubs(:newservice) + end + + it "should exit if no defined --client" do + $stderr.stubs(:puts) + @puppetd.options.stubs(:[]).with(:client).returns(nil) + + @puppetd.expects(:exit).with(43) + + @puppetd.onetime + end + + it "should setup traps" do + @daemon.expects(:set_signal_traps) + + @puppetd.onetime + end + + it "should let the agent run" do + @agent.expects(:run).returns(:report) + + @puppetd.onetime + end + + it "should finish by exiting with 0 error code" do + @puppetd.expects(:exit).with(0) + + @puppetd.onetime + end + + describe "and --detailed-exitcodes" do + before :each do + @puppetd.options.stubs(:[]).with(:detailed_exitcodes).returns(true) + end + + it "should exit with report's computed exit status" do + Puppet.stubs(:[]).with(:noop).returns(false) + report = stub 'report', :exit_status => 666 + @agent.stubs(:run).returns(report) + @puppetd.expects(:exit).with(666) + + @puppetd.onetime + end + + it "should always exit with 0 if --noop" do + Puppet.stubs(:[]).with(:noop).returns(true) + report = stub 'report', :exit_status => 666 + @agent.stubs(:run).returns(report) + @puppetd.expects(:exit).with(0) + + @puppetd.onetime + end + end + end + + describe "with --fingerprint" do + before :each do + @cert = stub_everything 'cert' + @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) + @puppetd.options.stubs(:[]).with(:digest).returns(:MD5) + @host = stub_everything 'host' + @puppetd.stubs(:host).returns(@host) + end + + it "should fingerprint the certificate if it exists" do + @host.expects(:certificate).returns(@cert) + @cert.expects(:fingerprint).with(:MD5).returns "fingerprint" + @puppetd.fingerprint + end + + it "should fingerprint the certificate request if no certificate have been signed" do + @host.expects(:certificate).returns(nil) + @host.expects(:certificate_request).returns(@cert) + @cert.expects(:fingerprint).with(:MD5).returns "fingerprint" + @puppetd.fingerprint + end + + it "should display the fingerprint" do + @host.stubs(:certificate).returns(@cert) + @cert.stubs(:fingerprint).with(:MD5).returns("DIGEST") + + Puppet.expects(:notice).with("DIGEST") + + @puppetd.fingerprint + end + end + + describe "without --onetime and --fingerprint" do + before :each do + Puppet.stubs(:notice) + @puppetd.options.stubs(:[]).with(:client) + end + + it "should start our daemon" do + @daemon.expects(:start) + + @puppetd.main + end + end + end +end diff --git a/spec/unit/application/agent_spec_spec.rb b/spec/unit/application/agent_spec_spec.rb deleted file mode 100755 index 76a378e82..000000000 --- a/spec/unit/application/agent_spec_spec.rb +++ /dev/null @@ -1,600 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/agent' -require 'puppet/application/agent' -require 'puppet/network/server' -require 'puppet/daemon' - -describe Puppet::Application::Agent do - before :each do - @puppetd = Puppet::Application[:agent] - @puppetd.stubs(:puts) - @daemon = stub_everything 'daemon' - Puppet::Daemon.stubs(:new).returns(@daemon) - @agent = stub_everything 'agent' - Puppet::Agent.stubs(:new).returns(@agent) - @puppetd.preinit - Puppet::Util::Log.stubs(:newdestination) - Puppet::Util::Log.stubs(:level=) - - Puppet::Node.stubs(:terminus_class=) - Puppet::Node.stubs(:cache_class=) - Puppet::Node::Facts.stubs(:terminus_class=) - end - - it "should ask Puppet::Application to parse Puppet configuration file" do - @puppetd.should_parse_config?.should be_true - end - - it "should declare a main command" do - @puppetd.should respond_to(:main) - end - - it "should declare a onetime command" do - @puppetd.should respond_to(:onetime) - end - - it "should declare a fingerprint command" do - @puppetd.should respond_to(:fingerprint) - end - - it "should declare a preinit block" do - @puppetd.should respond_to(:preinit) - end - - describe "in preinit" do - before :each do - @puppetd.stubs(:trap) - end - - it "should catch INT" do - @puppetd.expects(:trap).with { |arg,block| arg == :INT } - - @puppetd.preinit - end - - it "should set waitforcert to 120" do - @puppetd.preinit - - @puppetd.options[:waitforcert].should == 120 - end - - it "should init client to true" do - @puppetd.preinit - - @puppetd.options[:client].should be_true - end - - it "should init fqdn to nil" do - @puppetd.preinit - - @puppetd.options[:fqdn].should be_nil - end - - it "should init serve to []" do - @puppetd.preinit - - @puppetd.options[:serve].should == [] - end - - it "should use MD5 as default digest algorithm" do - @puppetd.preinit - - @puppetd.options[:digest].should == :MD5 - end - - it "should not fingerprint by default" do - @puppetd.preinit - - @puppetd.options[:fingerprint].should be_false - end - end - - describe "when handling options" do - before do - @puppetd.command_line.stubs(:args).returns([]) - end - - [:centrallogging, :disable, :enable, :debug, :fqdn, :test, :verbose, :digest].each do |option| - it "should declare handle_#{option} method" do - @puppetd.should respond_to("handle_#{option}".to_sym) - end - - it "should store argument value when calling handle_#{option}" do - @puppetd.options.expects(:[]=).with(option, 'arg') - @puppetd.send("handle_#{option}".to_sym, 'arg') - end - end - - it "should set an existing handler on server" do - Puppet::Network::Handler.stubs(:handler).with("handler").returns(true) - - @puppetd.handle_serve("handler") - @puppetd.options[:serve].should == [ :handler ] - end - - it "should set client to false with --no-client" do - @puppetd.handle_no_client(nil) - @puppetd.options[:client].should be_false - end - - it "should set onetime to true with --onetime" do - @puppetd.handle_onetime(nil) - Puppet[:onetime].should be_true - end - - it "should set waitforcert to 0 with --onetime and if --waitforcert wasn't given" do - @puppetd.explicit_waitforcert = false - @puppetd.handle_onetime(nil) - @puppetd.options[:waitforcert].should == 0 - end - - it "should not reset waitforcert with --onetime when --waitforcert is used" do - @puppetd.explicit_waitforcert = true - @puppetd.handle_onetime(nil) - @puppetd.options[:waitforcert].should_not == 0 - end - - it "should set the log destination with --logdest" do - @puppetd.options.stubs(:[]=).with { |opt,val| opt == :setdest } - Puppet::Log.expects(:newdestination).with("console") - - @puppetd.handle_logdest("console") - end - - it "should put the setdest options to true" do - @puppetd.options.expects(:[]=).with(:setdest,true) - - @puppetd.handle_logdest("console") - end - - it "should parse the log destination from the command line" do - @puppetd.command_line.stubs(:args).returns(%w[--logdest /my/file]) - - Puppet::Util::Log.expects(:newdestination).with("/my/file") - - @puppetd.parse_options - end - - it "should store the waitforcert options with --waitforcert" do - @puppetd.options.expects(:[]=).with(:waitforcert,42) - - @puppetd.handle_waitforcert("42") - end - - it "should mark explicit_waitforcert to true with --waitforcert" do - @puppetd.options.stubs(:[]=) - - @puppetd.handle_waitforcert("42") - @puppetd.explicit_waitforcert.should be_true - end - - it "should set args[:Port] with --port" do - @puppetd.handle_port("42") - @puppetd.args[:Port].should == "42" - end - - end - - describe "during setup" do - before :each do - @puppetd.options.stubs(:[]) - Puppet.stubs(:info) - FileTest.stubs(:exists?).returns(true) - Puppet.stubs(:[]) - Puppet.stubs(:[]=) - Puppet.stubs(:[]).with(:libdir).returns("/dev/null/lib") - Puppet.settings.stubs(:print_config?) - Puppet.settings.stubs(:print_config) - Puppet::SSL::Host.stubs(:ca_location=) - Puppet::Transaction::Report.stubs(:terminus_class=) - Puppet::Resource::Catalog.stubs(:terminus_class=) - Puppet::Resource::Catalog.stubs(:cache_class=) - Puppet::Node::Facts.stubs(:terminus_class=) - @host = stub_everything 'host' - Puppet::SSL::Host.stubs(:new).returns(@host) - Puppet.stubs(:settraps) - end - - describe "with --test" do - before :each do - Puppet.settings.stubs(:handlearg) - @puppetd.options.stubs(:[]=) - end - - it "should call setup_test" do - @puppetd.options.stubs(:[]).with(:test).returns(true) - @puppetd.expects(:setup_test) - @puppetd.setup - end - - it "should set options[:verbose] to true" do - @puppetd.options.expects(:[]=).with(:verbose,true) - @puppetd.setup_test - end - it "should set options[:onetime] to true" do - Puppet.expects(:[]=).with(:onetime,true) - @puppetd.setup_test - end - it "should set options[:detailed_exitcodes] to true" do - @puppetd.options.expects(:[]=).with(:detailed_exitcodes,true) - @puppetd.setup_test - end - it "should set waitforcert to 0" do - @puppetd.options.expects(:[]=).with(:waitforcert,0) - @puppetd.setup_test - end - end - - it "should call setup_logs" do - @puppetd.expects(:setup_logs) - @puppetd.setup - end - - describe "when setting up logs" do - before :each do - Puppet::Util::Log.stubs(:newdestination) - end - - it "should set log level to debug if --debug was passed" do - @puppetd.options.stubs(:[]).with(:debug).returns(true) - - Puppet::Util::Log.expects(:level=).with(:debug) - - @puppetd.setup_logs - end - - it "should set log level to info if --verbose was passed" do - @puppetd.options.stubs(:[]).with(:verbose).returns(true) - - Puppet::Util::Log.expects(:level=).with(:info) - - @puppetd.setup_logs - end - - [:verbose, :debug].each do |level| - it "should set console as the log destination with level #{level}" do - @puppetd.options.stubs(:[]).with(level).returns(true) - - Puppet::Util::Log.expects(:newdestination).with(:console) - - @puppetd.setup_logs - end - end - - it "should set syslog as the log destination if no --logdest" do - @puppetd.options.stubs(:[]).with(:setdest).returns(false) - - Puppet::Util::Log.expects(:newdestination).with(:syslog) - - @puppetd.setup_logs - end - - end - - it "should print puppet config if asked to in Puppet config" do - @puppetd.stubs(:exit) - Puppet.settings.stubs(:print_configs?).returns(true) - - Puppet.settings.expects(:print_configs) - - @puppetd.setup - end - - it "should exit after printing puppet config if asked to in Puppet config" do - Puppet.settings.stubs(:print_configs?).returns(true) - - lambda { @puppetd.setup }.should raise_error(SystemExit) - end - - it "should set a central log destination with --centrallogs" do - @puppetd.options.stubs(:[]).with(:centrallogs).returns(true) - Puppet.stubs(:[]).with(:server).returns("puppet.reductivelabs.com") - Puppet::Util::Log.stubs(:newdestination).with(:syslog) - - Puppet::Util::Log.expects(:newdestination).with("puppet.reductivelabs.com") - - @puppetd.setup - end - - it "should use :main, :puppetd, and :ssl" do - Puppet.settings.expects(:use).with(:main, :agent, :ssl) - - @puppetd.setup - end - - it "should install a remote ca location" do - Puppet::SSL::Host.expects(:ca_location=).with(:remote) - - @puppetd.setup - end - - it "should install a none ca location in fingerprint mode" do - @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) - Puppet::SSL::Host.expects(:ca_location=).with(:none) - - @puppetd.setup - end - - it "should tell the report handler to use REST" do - Puppet::Transaction::Report.expects(:terminus_class=).with(:rest) - - @puppetd.setup - end - - it "should change the catalog_terminus setting to 'rest'" do - Puppet.expects(:[]=).with(:catalog_terminus, :rest) - @puppetd.setup - end - - it "should tell the catalog handler to use cache" do - Puppet::Resource::Catalog.expects(:cache_class=).with(:yaml) - - @puppetd.setup - end - - it "should change the facts_terminus setting to 'facter'" do - Puppet.expects(:[]=).with(:facts_terminus, :facter) - - @puppetd.setup - end - - it "should create an agent" do - Puppet::Agent.stubs(:new).with(Puppet::Configurer) - - @puppetd.setup - end - - [:enable, :disable].each do |action| - it "should delegate to enable_disable_client if we #{action} the agent" do - @puppetd.options.stubs(:[]).with(action).returns(true) - @puppetd.expects(:enable_disable_client).with(@agent) - - @puppetd.setup - end - end - - describe "when enabling or disabling agent" do - [:enable, :disable].each do |action| - it "should call client.#{action}" do - @puppetd.stubs(:exit) - @puppetd.options.stubs(:[]).with(action).returns(true) - - @agent.expects(action) - - @puppetd.enable_disable_client(@agent) - end - end - - it "should finally exit" do - lambda { @puppetd.enable_disable_client(@agent) }.should raise_error(SystemExit) - end - end - - it "should inform the daemon about our agent if :client is set to 'true'" do - @puppetd.options.expects(:[]).with(:client).returns true - @daemon.expects(:agent=).with(@agent) - @puppetd.setup - end - - it "should not inform the daemon about our agent if :client is set to 'false'" do - @puppetd.options[:client] = false - @daemon.expects(:agent=).never - @puppetd.setup - end - - it "should daemonize if needed" do - Puppet.stubs(:[]).with(:daemonize).returns(true) - - @daemon.expects(:daemonize) - - @puppetd.setup - end - - it "should wait for a certificate" do - @puppetd.options.stubs(:[]).with(:waitforcert).returns(123) - @host.expects(:wait_for_cert).with(123) - - @puppetd.setup - end - - it "should not wait for a certificate in fingerprint mode" do - @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) - @puppetd.options.stubs(:[]).with(:waitforcert).returns(123) - @host.expects(:wait_for_cert).never - - @puppetd.setup - end - - it "should setup listen if told to and not onetime" do - Puppet.stubs(:[]).with(:listen).returns(true) - @puppetd.options.stubs(:[]).with(:onetime).returns(false) - - @puppetd.expects(:setup_listen) - - @puppetd.setup - end - - describe "when setting up listen" do - before :each do - Puppet.stubs(:[]).with(:authconfig).returns('auth') - FileTest.stubs(:exists?).with('auth').returns(true) - File.stubs(:exist?).returns(true) - @puppetd.options.stubs(:[]).with(:serve).returns([]) - @puppetd.stubs(:exit) - @server = stub_everything 'server' - Puppet::Network::Server.stubs(:new).returns(@server) - end - - - it "should exit if no authorization file" do - Puppet.stubs(:err) - FileTest.stubs(:exists?).with('auth').returns(false) - - @puppetd.expects(:exit) - - @puppetd.setup_listen - end - - it "should create a server to listen on at least the Runner handler" do - Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Runner] } - - @puppetd.setup_listen - end - - it "should create a server to listen for specific handlers" do - @puppetd.options.stubs(:[]).with(:serve).returns([:handler]) - Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:handler] } - - @puppetd.setup_listen - end - - it "should use puppet default port" do - Puppet.stubs(:[]).with(:puppetport).returns(:port) - - Puppet::Network::Server.expects(:new).with { |args| args[:port] == :port } - - @puppetd.setup_listen - end - end - end - - - describe "when running" do - before :each do - @puppetd.agent = @agent - @puppetd.daemon = @daemon - @puppetd.options.stubs(:[]).with(:fingerprint).returns(false) - end - - it "should dispatch to fingerprint if --fingerprint is used" do - @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) - - @puppetd.stubs(:fingerprint) - @puppetd.run_command - end - - it "should dispatch to onetime if --onetime is used" do - @puppetd.options.stubs(:[]).with(:onetime).returns(true) - - @puppetd.stubs(:onetime) - @puppetd.run_command - end - - it "should dispatch to main if --onetime and --fingerprint are not used" do - @puppetd.options.stubs(:[]).with(:onetime).returns(false) - - @puppetd.stubs(:main) - @puppetd.run_command - end - - describe "with --onetime" do - - before :each do - @agent.stubs(:run).returns(:report) - @puppetd.options.stubs(:[]).with(:client).returns(:client) - @puppetd.options.stubs(:[]).with(:detailed_exitcodes).returns(false) - @puppetd.stubs(:exit).with(0) - Puppet.stubs(:newservice) - end - - it "should exit if no defined --client" do - $stderr.stubs(:puts) - @puppetd.options.stubs(:[]).with(:client).returns(nil) - - @puppetd.expects(:exit).with(43) - - @puppetd.onetime - end - - it "should setup traps" do - @daemon.expects(:set_signal_traps) - - @puppetd.onetime - end - - it "should let the agent run" do - @agent.expects(:run).returns(:report) - - @puppetd.onetime - end - - it "should finish by exiting with 0 error code" do - @puppetd.expects(:exit).with(0) - - @puppetd.onetime - end - - describe "and --detailed-exitcodes" do - before :each do - @puppetd.options.stubs(:[]).with(:detailed_exitcodes).returns(true) - end - - it "should exit with report's computed exit status" do - Puppet.stubs(:[]).with(:noop).returns(false) - report = stub 'report', :exit_status => 666 - @agent.stubs(:run).returns(report) - @puppetd.expects(:exit).with(666) - - @puppetd.onetime - end - - it "should always exit with 0 if --noop" do - Puppet.stubs(:[]).with(:noop).returns(true) - report = stub 'report', :exit_status => 666 - @agent.stubs(:run).returns(report) - @puppetd.expects(:exit).with(0) - - @puppetd.onetime - end - end - end - - describe "with --fingerprint" do - before :each do - @cert = stub_everything 'cert' - @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) - @puppetd.options.stubs(:[]).with(:digest).returns(:MD5) - @host = stub_everything 'host' - @puppetd.stubs(:host).returns(@host) - end - - it "should fingerprint the certificate if it exists" do - @host.expects(:certificate).returns(@cert) - @cert.expects(:fingerprint).with(:MD5).returns "fingerprint" - @puppetd.fingerprint - end - - it "should fingerprint the certificate request if no certificate have been signed" do - @host.expects(:certificate).returns(nil) - @host.expects(:certificate_request).returns(@cert) - @cert.expects(:fingerprint).with(:MD5).returns "fingerprint" - @puppetd.fingerprint - end - - it "should display the fingerprint" do - @host.stubs(:certificate).returns(@cert) - @cert.stubs(:fingerprint).with(:MD5).returns("DIGEST") - - Puppet.expects(:notice).with("DIGEST") - - @puppetd.fingerprint - end - end - - describe "without --onetime and --fingerprint" do - before :each do - Puppet.stubs(:notice) - @puppetd.options.stubs(:[]).with(:client) - end - - it "should start our daemon" do - @daemon.expects(:start) - - @puppetd.main - end - end - end -end diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb new file mode 100755 index 000000000..a1a7e7b31 --- /dev/null +++ b/spec/unit/application/apply_spec.rb @@ -0,0 +1,387 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/application/apply' + +describe Puppet::Application::Apply do + before :each do + @apply = Puppet::Application[:apply] + Puppet::Util::Log.stubs(:newdestination) + Puppet::Util::Log.stubs(:level=) + end + + [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes].each do |option| + it "should declare handle_#{option} method" do + @apply.should respond_to("handle_#{option}".to_sym) + end + + it "should store argument value when calling handle_#{option}" do + @apply.options.expects(:[]=).with(option, 'arg') + @apply.send("handle_#{option}".to_sym, 'arg') + end + end + + it "should set the code to the provided code when :execute is used" do + @apply.options.expects(:[]=).with(:code, 'arg') + @apply.send("handle_execute".to_sym, 'arg') + end + + it "should ask Puppet::Application to parse Puppet configuration file" do + @apply.should_parse_config?.should be_true + end + + describe "when applying options" do + + it "should set the log destination with --logdest" do + Puppet::Log.expects(:newdestination).with("console") + + @apply.handle_logdest("console") + end + + it "should put the logset options to true" do + @apply.options.expects(:[]=).with(:logset,true) + + @apply.handle_logdest("console") + end + end + + describe "during setup" do + + before :each do + Puppet::Log.stubs(:newdestination) + Puppet.stubs(:trap) + Puppet::Log.stubs(:level=) + Puppet.stubs(:parse_config) + require 'lib/puppet/file_bucket/dipper' + Puppet::FileBucket::Dipper.stubs(:new) + STDIN.stubs(:read) + + @apply.options.stubs(:[]).with(any_parameters) + end + + it "should set show_diff on --noop" do + Puppet.stubs(:[]=) + Puppet.stubs(:[]).with(:config) + Puppet.stubs(:[]).with(:noop).returns(true) + + Puppet.expects(:[]=).with(:show_diff, true) + + @apply.setup + end + + it "should set console as the log destination if logdest option wasn't provided" do + Puppet::Log.expects(:newdestination).with(:console) + + @apply.setup + end + + it "should set INT trap" do + @apply.expects(:trap).with(:INT) + + @apply.setup + end + + it "should set log level to debug if --debug was passed" do + @apply.options.stubs(:[]).with(:debug).returns(true) + + Puppet::Log.expects(:level=).with(:debug) + + @apply.setup + end + + it "should set log level to info if --verbose was passed" do + @apply.options.stubs(:[]).with(:verbose).returns(true) + + Puppet::Log.expects(:level=).with(:info) + + @apply.setup + end + + it "should print puppet config if asked to in Puppet config" do + @apply.stubs(:exit) + Puppet.settings.stubs(:print_configs?).returns(true) + + Puppet.settings.expects(:print_configs) + + @apply.setup + end + + it "should exit after printing puppet config if asked to in Puppet config" do + Puppet.settings.stubs(:print_configs?).returns(true) + + lambda { @apply.setup }.should raise_error(SystemExit) + end + + end + + describe "when executing" do + + it "should dispatch to parseonly if parseonly is set" do + @apply.stubs(:options).returns({}) + Puppet.stubs(:[]).with(:parseonly).returns(true) + + @apply.expects(:parseonly) + @apply.run_command + end + + it "should dispatch to 'apply' if it was called with 'apply'" do + @apply.options[:catalog] = "foo" + + @apply.expects(:apply) + @apply.run_command + end + + it "should dispatch to main if parseonly is not set" do + @apply.stubs(:options).returns({}) + Puppet.stubs(:[]).with(:parseonly).returns(false) + + @apply.expects(:main) + @apply.run_command + end + + describe "the parseonly command" do + before :each do + Puppet.stubs(:[]).with(:environment) + Puppet.stubs(:[]).with(:manifest).returns("site.pp") + Puppet.stubs(:err) + @apply.stubs(:exit) + @apply.options.stubs(:[]).with(:code).returns "some code" + @collection = stub_everything + Puppet::Resource::TypeCollection.stubs(:new).returns(@collection) + end + + it "should use a Puppet Resource Type Collection to parse the file" do + @collection.expects(:perform_initial_import) + @apply.parseonly + end + + it "should exit with exit code 0 if no error" do + @apply.expects(:exit).with(0) + @apply.parseonly + end + + it "should exit with exit code 1 if error" do + @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) + @apply.expects(:exit).with(1) + @apply.parseonly + end + end + + describe "the main command" do + before :each do + Puppet.stubs(:[]) + Puppet.settings.stubs(:use) + Puppet.stubs(:[]).with(:prerun_command).returns "" + Puppet.stubs(:[]).with(:postrun_command).returns "" + Puppet.stubs(:[]).with(:trace).returns(true) + + @apply.options.stubs(:[]) + + @facts = stub_everything 'facts' + Puppet::Node::Facts.stubs(:find).returns(@facts) + + @node = stub_everything 'node' + Puppet::Node.stubs(:find).returns(@node) + + @catalog = stub_everything 'catalog' + @catalog.stubs(:to_ral).returns(@catalog) + Puppet::Resource::Catalog.stubs(:find).returns(@catalog) + + STDIN.stubs(:read) + + @transaction = stub_everything 'transaction' + @catalog.stubs(:apply).returns(@transaction) + + @apply.stubs(:exit) + end + + it "should set the code to run from --code" do + @apply.options.stubs(:[]).with(:code).returns("code to run") + Puppet.expects(:[]=).with(:code,"code to run") + + @apply.main + end + + it "should set the code to run from STDIN if no arguments" do + @apply.command_line.stubs(:args).returns([]) + STDIN.stubs(:read).returns("code to run") + + Puppet.expects(:[]=).with(:code,"code to run") + + @apply.main + end + + it "should set the manifest if some files are passed on command line" do + @apply.command_line.stubs(:args).returns(['site.pp']) + + Puppet.expects(:[]=).with(:manifest,"site.pp") + + @apply.main + end + + it "should collect the node facts" do + Puppet::Node::Facts.expects(:find).returns(@facts) + + @apply.main + end + + it "should raise an error if we can't find the node" do + Puppet::Node::Facts.expects(:find).returns(nil) + + lambda { @apply.main }.should raise_error + end + + it "should find the node" do + Puppet::Node.expects(:find).returns(@node) + + @apply.main + end + + it "should raise an error if we can't find the node" do + Puppet::Node.expects(:find).returns(nil) + + lambda { @apply.main }.should raise_error + end + + it "should merge in our node the loaded facts" do + @facts.stubs(:values).returns("values") + + @node.expects(:merge).with("values") + + @apply.main + end + + it "should load custom classes if loadclasses" do + @apply.options.stubs(:[]).with(:loadclasses).returns(true) + Puppet.stubs(:[]).with(:classfile).returns("/etc/puppet/classes.txt") + FileTest.stubs(:exists?).with("/etc/puppet/classes.txt").returns(true) + FileTest.stubs(:readable?).with("/etc/puppet/classes.txt").returns(true) + File.stubs(:read).with("/etc/puppet/classes.txt").returns("class") + + @node.expects(:classes=) + + @apply.main + end + + it "should compile the catalog" do + Puppet::Resource::Catalog.expects(:find).returns(@catalog) + + @apply.main + end + + it "should transform the catalog to ral" do + + @catalog.expects(:to_ral).returns(@catalog) + + @apply.main + end + + it "should finalize the catalog" do + @catalog.expects(:finalize) + + @apply.main + end + + it "should call the prerun and postrun commands on a Configurer instance" do + configurer = stub 'configurer' + + Puppet::Configurer.expects(:new).returns configurer + configurer.expects(:execute_prerun_command) + configurer.expects(:execute_postrun_command) + + @apply.main + end + + it "should apply the catalog" do + @catalog.expects(:apply) + + @apply.main + end + + describe "with detailed_exitcodes" do + it "should exit with report's computed exit status" do + Puppet.stubs(:[]).with(:noop).returns(false) + @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) + report = stub 'report', :exit_status => 666 + @transaction.stubs(:report).returns(report) + @apply.expects(:exit).with(666) + + @apply.main + end + + it "should always exit with 0 if option is disabled" do + Puppet.stubs(:[]).with(:noop).returns(false) + @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(false) + report = stub 'report', :exit_status => 666 + @transaction.stubs(:report).returns(report) + @apply.expects(:exit).with(0) + + @apply.main + end + + it "should always exit with 0 if --noop" do + Puppet.stubs(:[]).with(:noop).returns(true) + @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) + report = stub 'report', :exit_status => 666 + @transaction.stubs(:report).returns(report) + @apply.expects(:exit).with(0) + + @apply.main + end + end + end + + describe "the 'apply' command" do + it "should read the catalog in from disk if a file name is provided" do + @apply.options[:catalog] = "/my/catalog.pson" + File.expects(:read).with("/my/catalog.pson").returns "something" + Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new + @apply.apply + end + + it "should read the catalog in from stdin if '-' is provided" do + @apply.options[:catalog] = "-" + $stdin.expects(:read).returns "something" + Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new + @apply.apply + end + + it "should deserialize the catalog from the default format" do + @apply.options[:catalog] = "/my/catalog.pson" + File.stubs(:read).with("/my/catalog.pson").returns "something" + Puppet::Resource::Catalog.stubs(:default_format).returns :rot13_piglatin + Puppet::Resource::Catalog.stubs(:convert_from).with(:rot13_piglatin,'something').returns Puppet::Resource::Catalog.new + @apply.apply + end + + it "should fail helpfully if deserializing fails" do + @apply.options[:catalog] = "/my/catalog.pson" + File.stubs(:read).with("/my/catalog.pson").returns "something syntacically invalid" + lambda { @apply.apply }.should raise_error(Puppet::Error) + end + + it "should convert plain data structures into a catalog if deserialization does not do so" do + @apply.options[:catalog] = "/my/catalog.pson" + File.stubs(:read).with("/my/catalog.pson").returns "something" + Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,"something").returns({:foo => "bar"}) + Puppet::Resource::Catalog.expects(:pson_create).with({:foo => "bar"}).returns(Puppet::Resource::Catalog.new) + @apply.apply + end + + it "should convert the catalog to a RAL catalog and use a Configurer instance to apply it" do + @apply.options[:catalog] = "/my/catalog.pson" + File.stubs(:read).with("/my/catalog.pson").returns "something" + catalog = Puppet::Resource::Catalog.new + Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns catalog + catalog.expects(:to_ral).returns "mycatalog" + + configurer = stub 'configurer' + Puppet::Configurer.expects(:new).returns configurer + configurer.expects(:run).with(:catalog => "mycatalog") + + @apply.apply + end + end + end +end diff --git a/spec/unit/application/apply_spec_spec.rb b/spec/unit/application/apply_spec_spec.rb deleted file mode 100755 index a1a7e7b31..000000000 --- a/spec/unit/application/apply_spec_spec.rb +++ /dev/null @@ -1,387 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/application/apply' - -describe Puppet::Application::Apply do - before :each do - @apply = Puppet::Application[:apply] - Puppet::Util::Log.stubs(:newdestination) - Puppet::Util::Log.stubs(:level=) - end - - [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes].each do |option| - it "should declare handle_#{option} method" do - @apply.should respond_to("handle_#{option}".to_sym) - end - - it "should store argument value when calling handle_#{option}" do - @apply.options.expects(:[]=).with(option, 'arg') - @apply.send("handle_#{option}".to_sym, 'arg') - end - end - - it "should set the code to the provided code when :execute is used" do - @apply.options.expects(:[]=).with(:code, 'arg') - @apply.send("handle_execute".to_sym, 'arg') - end - - it "should ask Puppet::Application to parse Puppet configuration file" do - @apply.should_parse_config?.should be_true - end - - describe "when applying options" do - - it "should set the log destination with --logdest" do - Puppet::Log.expects(:newdestination).with("console") - - @apply.handle_logdest("console") - end - - it "should put the logset options to true" do - @apply.options.expects(:[]=).with(:logset,true) - - @apply.handle_logdest("console") - end - end - - describe "during setup" do - - before :each do - Puppet::Log.stubs(:newdestination) - Puppet.stubs(:trap) - Puppet::Log.stubs(:level=) - Puppet.stubs(:parse_config) - require 'lib/puppet/file_bucket/dipper' - Puppet::FileBucket::Dipper.stubs(:new) - STDIN.stubs(:read) - - @apply.options.stubs(:[]).with(any_parameters) - end - - it "should set show_diff on --noop" do - Puppet.stubs(:[]=) - Puppet.stubs(:[]).with(:config) - Puppet.stubs(:[]).with(:noop).returns(true) - - Puppet.expects(:[]=).with(:show_diff, true) - - @apply.setup - end - - it "should set console as the log destination if logdest option wasn't provided" do - Puppet::Log.expects(:newdestination).with(:console) - - @apply.setup - end - - it "should set INT trap" do - @apply.expects(:trap).with(:INT) - - @apply.setup - end - - it "should set log level to debug if --debug was passed" do - @apply.options.stubs(:[]).with(:debug).returns(true) - - Puppet::Log.expects(:level=).with(:debug) - - @apply.setup - end - - it "should set log level to info if --verbose was passed" do - @apply.options.stubs(:[]).with(:verbose).returns(true) - - Puppet::Log.expects(:level=).with(:info) - - @apply.setup - end - - it "should print puppet config if asked to in Puppet config" do - @apply.stubs(:exit) - Puppet.settings.stubs(:print_configs?).returns(true) - - Puppet.settings.expects(:print_configs) - - @apply.setup - end - - it "should exit after printing puppet config if asked to in Puppet config" do - Puppet.settings.stubs(:print_configs?).returns(true) - - lambda { @apply.setup }.should raise_error(SystemExit) - end - - end - - describe "when executing" do - - it "should dispatch to parseonly if parseonly is set" do - @apply.stubs(:options).returns({}) - Puppet.stubs(:[]).with(:parseonly).returns(true) - - @apply.expects(:parseonly) - @apply.run_command - end - - it "should dispatch to 'apply' if it was called with 'apply'" do - @apply.options[:catalog] = "foo" - - @apply.expects(:apply) - @apply.run_command - end - - it "should dispatch to main if parseonly is not set" do - @apply.stubs(:options).returns({}) - Puppet.stubs(:[]).with(:parseonly).returns(false) - - @apply.expects(:main) - @apply.run_command - end - - describe "the parseonly command" do - before :each do - Puppet.stubs(:[]).with(:environment) - Puppet.stubs(:[]).with(:manifest).returns("site.pp") - Puppet.stubs(:err) - @apply.stubs(:exit) - @apply.options.stubs(:[]).with(:code).returns "some code" - @collection = stub_everything - Puppet::Resource::TypeCollection.stubs(:new).returns(@collection) - end - - it "should use a Puppet Resource Type Collection to parse the file" do - @collection.expects(:perform_initial_import) - @apply.parseonly - end - - it "should exit with exit code 0 if no error" do - @apply.expects(:exit).with(0) - @apply.parseonly - end - - it "should exit with exit code 1 if error" do - @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) - @apply.expects(:exit).with(1) - @apply.parseonly - end - end - - describe "the main command" do - before :each do - Puppet.stubs(:[]) - Puppet.settings.stubs(:use) - Puppet.stubs(:[]).with(:prerun_command).returns "" - Puppet.stubs(:[]).with(:postrun_command).returns "" - Puppet.stubs(:[]).with(:trace).returns(true) - - @apply.options.stubs(:[]) - - @facts = stub_everything 'facts' - Puppet::Node::Facts.stubs(:find).returns(@facts) - - @node = stub_everything 'node' - Puppet::Node.stubs(:find).returns(@node) - - @catalog = stub_everything 'catalog' - @catalog.stubs(:to_ral).returns(@catalog) - Puppet::Resource::Catalog.stubs(:find).returns(@catalog) - - STDIN.stubs(:read) - - @transaction = stub_everything 'transaction' - @catalog.stubs(:apply).returns(@transaction) - - @apply.stubs(:exit) - end - - it "should set the code to run from --code" do - @apply.options.stubs(:[]).with(:code).returns("code to run") - Puppet.expects(:[]=).with(:code,"code to run") - - @apply.main - end - - it "should set the code to run from STDIN if no arguments" do - @apply.command_line.stubs(:args).returns([]) - STDIN.stubs(:read).returns("code to run") - - Puppet.expects(:[]=).with(:code,"code to run") - - @apply.main - end - - it "should set the manifest if some files are passed on command line" do - @apply.command_line.stubs(:args).returns(['site.pp']) - - Puppet.expects(:[]=).with(:manifest,"site.pp") - - @apply.main - end - - it "should collect the node facts" do - Puppet::Node::Facts.expects(:find).returns(@facts) - - @apply.main - end - - it "should raise an error if we can't find the node" do - Puppet::Node::Facts.expects(:find).returns(nil) - - lambda { @apply.main }.should raise_error - end - - it "should find the node" do - Puppet::Node.expects(:find).returns(@node) - - @apply.main - end - - it "should raise an error if we can't find the node" do - Puppet::Node.expects(:find).returns(nil) - - lambda { @apply.main }.should raise_error - end - - it "should merge in our node the loaded facts" do - @facts.stubs(:values).returns("values") - - @node.expects(:merge).with("values") - - @apply.main - end - - it "should load custom classes if loadclasses" do - @apply.options.stubs(:[]).with(:loadclasses).returns(true) - Puppet.stubs(:[]).with(:classfile).returns("/etc/puppet/classes.txt") - FileTest.stubs(:exists?).with("/etc/puppet/classes.txt").returns(true) - FileTest.stubs(:readable?).with("/etc/puppet/classes.txt").returns(true) - File.stubs(:read).with("/etc/puppet/classes.txt").returns("class") - - @node.expects(:classes=) - - @apply.main - end - - it "should compile the catalog" do - Puppet::Resource::Catalog.expects(:find).returns(@catalog) - - @apply.main - end - - it "should transform the catalog to ral" do - - @catalog.expects(:to_ral).returns(@catalog) - - @apply.main - end - - it "should finalize the catalog" do - @catalog.expects(:finalize) - - @apply.main - end - - it "should call the prerun and postrun commands on a Configurer instance" do - configurer = stub 'configurer' - - Puppet::Configurer.expects(:new).returns configurer - configurer.expects(:execute_prerun_command) - configurer.expects(:execute_postrun_command) - - @apply.main - end - - it "should apply the catalog" do - @catalog.expects(:apply) - - @apply.main - end - - describe "with detailed_exitcodes" do - it "should exit with report's computed exit status" do - Puppet.stubs(:[]).with(:noop).returns(false) - @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) - report = stub 'report', :exit_status => 666 - @transaction.stubs(:report).returns(report) - @apply.expects(:exit).with(666) - - @apply.main - end - - it "should always exit with 0 if option is disabled" do - Puppet.stubs(:[]).with(:noop).returns(false) - @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(false) - report = stub 'report', :exit_status => 666 - @transaction.stubs(:report).returns(report) - @apply.expects(:exit).with(0) - - @apply.main - end - - it "should always exit with 0 if --noop" do - Puppet.stubs(:[]).with(:noop).returns(true) - @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) - report = stub 'report', :exit_status => 666 - @transaction.stubs(:report).returns(report) - @apply.expects(:exit).with(0) - - @apply.main - end - end - end - - describe "the 'apply' command" do - it "should read the catalog in from disk if a file name is provided" do - @apply.options[:catalog] = "/my/catalog.pson" - File.expects(:read).with("/my/catalog.pson").returns "something" - Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new - @apply.apply - end - - it "should read the catalog in from stdin if '-' is provided" do - @apply.options[:catalog] = "-" - $stdin.expects(:read).returns "something" - Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new - @apply.apply - end - - it "should deserialize the catalog from the default format" do - @apply.options[:catalog] = "/my/catalog.pson" - File.stubs(:read).with("/my/catalog.pson").returns "something" - Puppet::Resource::Catalog.stubs(:default_format).returns :rot13_piglatin - Puppet::Resource::Catalog.stubs(:convert_from).with(:rot13_piglatin,'something').returns Puppet::Resource::Catalog.new - @apply.apply - end - - it "should fail helpfully if deserializing fails" do - @apply.options[:catalog] = "/my/catalog.pson" - File.stubs(:read).with("/my/catalog.pson").returns "something syntacically invalid" - lambda { @apply.apply }.should raise_error(Puppet::Error) - end - - it "should convert plain data structures into a catalog if deserialization does not do so" do - @apply.options[:catalog] = "/my/catalog.pson" - File.stubs(:read).with("/my/catalog.pson").returns "something" - Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,"something").returns({:foo => "bar"}) - Puppet::Resource::Catalog.expects(:pson_create).with({:foo => "bar"}).returns(Puppet::Resource::Catalog.new) - @apply.apply - end - - it "should convert the catalog to a RAL catalog and use a Configurer instance to apply it" do - @apply.options[:catalog] = "/my/catalog.pson" - File.stubs(:read).with("/my/catalog.pson").returns "something" - catalog = Puppet::Resource::Catalog.new - Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns catalog - catalog.expects(:to_ral).returns "mycatalog" - - configurer = stub 'configurer' - Puppet::Configurer.expects(:new).returns configurer - configurer.expects(:run).with(:catalog => "mycatalog") - - @apply.apply - end - end - end -end diff --git a/spec/unit/application/cert_spec.rb b/spec/unit/application/cert_spec.rb new file mode 100644 index 000000000..ba6ba1d2d --- /dev/null +++ b/spec/unit/application/cert_spec.rb @@ -0,0 +1,172 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/application/cert' + +describe Puppet::Application::Cert do + before :each do + @cert_app = Puppet::Application[:cert] + Puppet::Util::Log.stubs(:newdestination) + Puppet::Util::Log.stubs(:level=) + end + + it "should ask Puppet::Application to parse Puppet configuration file" do + @cert_app.should_parse_config?.should be_true + end + + it "should declare a main command" do + @cert_app.should respond_to(:main) + end + + Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject{ |m| m == :destroy }.each do |method| + it "should declare option --#{method}" do + @cert_app.should respond_to("handle_#{method}".to_sym) + end + end + + it "should set log level to info with the --verbose option" do + + Puppet::Log.expects(:level=).with(:info) + + @cert_app.handle_verbose(0) + end + + it "should set log level to debug with the --debug option" do + + Puppet::Log.expects(:level=).with(:debug) + + @cert_app.handle_debug(0) + end + + it "should set the fingerprint digest with the --digest option" do + @cert_app.handle_digest(:digest) + + @cert_app.digest.should == :digest + end + + it "should set mode to :destroy for --clean" do + @cert_app.handle_clean(0) + @cert_app.mode.should == :destroy + end + + it "should set all to true for --all" do + @cert_app.handle_all(0) + @cert_app.all.should be_true + end + + it "should set signed to true for --signed" do + @cert_app.handle_signed(0) + @cert_app.signed.should be_true + end + + Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject { |m| m == :destroy }.each do |method| + it "should set mode to #{method} with option --#{method}" do + @cert_app.send("handle_#{method}".to_sym, nil) + + @cert_app.mode.should == method + end + end + + describe "during setup" do + + before :each do + Puppet::Log.stubs(:newdestination) + Puppet::SSL::Host.stubs(:ca_location=) + Puppet::SSL::CertificateAuthority.stubs(:new) + end + + it "should set console as the log destination" do + Puppet::Log.expects(:newdestination).with(:console) + + @cert_app.setup + end + + it "should print puppet config if asked to in Puppet config" do + @cert_app.stubs(:exit) + Puppet.settings.stubs(:print_configs?).returns(true) + + Puppet.settings.expects(:print_configs) + + @cert_app.setup + end + + it "should exit after printing puppet config if asked to in Puppet config" do + Puppet.settings.stubs(:print_configs?).returns(true) + + lambda { @cert_app.setup }.should raise_error(SystemExit) + end + + it "should set the CA location to 'only'" do + Puppet::SSL::Host.expects(:ca_location=).with(:only) + + @cert_app.setup + end + + it "should create a new certificate authority" do + Puppet::SSL::CertificateAuthority.expects(:new) + + @cert_app.setup + end + end + + describe "when running" do + before :each do + @cert_app.all = false + @ca = stub_everything 'ca' + @cert_app.ca = @ca + @cert_app.command_line.stubs(:args).returns([]) + end + + it "should delegate to the CertificateAuthority" do + @ca.expects(:apply) + + @cert_app.main + end + + it "should delegate with :all if option --all was given" do + @cert_app.handle_all(0) + + @ca.expects(:apply).with { |mode,to| to[:to] == :all } + + @cert_app.main + end + + it "should delegate to ca.apply with the hosts given on command line" do + @cert_app.command_line.stubs(:args).returns(["host"]) + + @ca.expects(:apply).with { |mode,to| to[:to] == ["host"]} + + @cert_app.main + end + + it "should send the currently set digest" do + @cert_app.command_line.stubs(:args).returns(["host"]) + @cert_app.handle_digest(:digest) + + @ca.expects(:apply).with { |mode,to| to[:digest] == :digest} + + @cert_app.main + end + + it "should delegate to ca.apply with current set mode" do + @cert_app.mode = "currentmode" + @cert_app.command_line.stubs(:args).returns(["host"]) + + @ca.expects(:apply).with { |mode,to| mode == "currentmode" } + + @cert_app.main + end + + it "should revoke cert if mode is clean" do + @cert_app.mode = :destroy + @cert_app.command_line.stubs(:args).returns(["host"]) + + @ca.expects(:apply).with { |mode,to| mode == :revoke } + @ca.expects(:apply).with { |mode,to| mode == :destroy } + + @cert_app.main + end + + end +end diff --git a/spec/unit/application/cert_spec_spec.rb b/spec/unit/application/cert_spec_spec.rb deleted file mode 100644 index ba6ba1d2d..000000000 --- a/spec/unit/application/cert_spec_spec.rb +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/application/cert' - -describe Puppet::Application::Cert do - before :each do - @cert_app = Puppet::Application[:cert] - Puppet::Util::Log.stubs(:newdestination) - Puppet::Util::Log.stubs(:level=) - end - - it "should ask Puppet::Application to parse Puppet configuration file" do - @cert_app.should_parse_config?.should be_true - end - - it "should declare a main command" do - @cert_app.should respond_to(:main) - end - - Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject{ |m| m == :destroy }.each do |method| - it "should declare option --#{method}" do - @cert_app.should respond_to("handle_#{method}".to_sym) - end - end - - it "should set log level to info with the --verbose option" do - - Puppet::Log.expects(:level=).with(:info) - - @cert_app.handle_verbose(0) - end - - it "should set log level to debug with the --debug option" do - - Puppet::Log.expects(:level=).with(:debug) - - @cert_app.handle_debug(0) - end - - it "should set the fingerprint digest with the --digest option" do - @cert_app.handle_digest(:digest) - - @cert_app.digest.should == :digest - end - - it "should set mode to :destroy for --clean" do - @cert_app.handle_clean(0) - @cert_app.mode.should == :destroy - end - - it "should set all to true for --all" do - @cert_app.handle_all(0) - @cert_app.all.should be_true - end - - it "should set signed to true for --signed" do - @cert_app.handle_signed(0) - @cert_app.signed.should be_true - end - - Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject { |m| m == :destroy }.each do |method| - it "should set mode to #{method} with option --#{method}" do - @cert_app.send("handle_#{method}".to_sym, nil) - - @cert_app.mode.should == method - end - end - - describe "during setup" do - - before :each do - Puppet::Log.stubs(:newdestination) - Puppet::SSL::Host.stubs(:ca_location=) - Puppet::SSL::CertificateAuthority.stubs(:new) - end - - it "should set console as the log destination" do - Puppet::Log.expects(:newdestination).with(:console) - - @cert_app.setup - end - - it "should print puppet config if asked to in Puppet config" do - @cert_app.stubs(:exit) - Puppet.settings.stubs(:print_configs?).returns(true) - - Puppet.settings.expects(:print_configs) - - @cert_app.setup - end - - it "should exit after printing puppet config if asked to in Puppet config" do - Puppet.settings.stubs(:print_configs?).returns(true) - - lambda { @cert_app.setup }.should raise_error(SystemExit) - end - - it "should set the CA location to 'only'" do - Puppet::SSL::Host.expects(:ca_location=).with(:only) - - @cert_app.setup - end - - it "should create a new certificate authority" do - Puppet::SSL::CertificateAuthority.expects(:new) - - @cert_app.setup - end - end - - describe "when running" do - before :each do - @cert_app.all = false - @ca = stub_everything 'ca' - @cert_app.ca = @ca - @cert_app.command_line.stubs(:args).returns([]) - end - - it "should delegate to the CertificateAuthority" do - @ca.expects(:apply) - - @cert_app.main - end - - it "should delegate with :all if option --all was given" do - @cert_app.handle_all(0) - - @ca.expects(:apply).with { |mode,to| to[:to] == :all } - - @cert_app.main - end - - it "should delegate to ca.apply with the hosts given on command line" do - @cert_app.command_line.stubs(:args).returns(["host"]) - - @ca.expects(:apply).with { |mode,to| to[:to] == ["host"]} - - @cert_app.main - end - - it "should send the currently set digest" do - @cert_app.command_line.stubs(:args).returns(["host"]) - @cert_app.handle_digest(:digest) - - @ca.expects(:apply).with { |mode,to| to[:digest] == :digest} - - @cert_app.main - end - - it "should delegate to ca.apply with current set mode" do - @cert_app.mode = "currentmode" - @cert_app.command_line.stubs(:args).returns(["host"]) - - @ca.expects(:apply).with { |mode,to| mode == "currentmode" } - - @cert_app.main - end - - it "should revoke cert if mode is clean" do - @cert_app.mode = :destroy - @cert_app.command_line.stubs(:args).returns(["host"]) - - @ca.expects(:apply).with { |mode,to| mode == :revoke } - @ca.expects(:apply).with { |mode,to| mode == :destroy } - - @cert_app.main - end - - end -end diff --git a/spec/unit/application/describe_spec.rb b/spec/unit/application/describe_spec.rb new file mode 100755 index 000000000..002cdb763 --- /dev/null +++ b/spec/unit/application/describe_spec.rb @@ -0,0 +1,84 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/application/describe' + +describe Puppet::Application::Describe do + before :each do + @describe = Puppet::Application[:describe] + end + + it "should ask Puppet::Application to not parse Puppet configuration file" do + @describe.should_parse_config?.should be_false + end + + it "should declare a main command" do + @describe.should respond_to(:main) + end + + it "should declare a preinit block" do + @describe.should respond_to(:preinit) + end + + [:providers,:list,:meta].each do |option| + it "should declare handle_#{option} method" do + @describe.should respond_to("handle_#{option}".to_sym) + end + + it "should store argument value when calling handle_#{option}" do + @describe.options.expects(:[]=).with("#{option}".to_sym, 'arg') + @describe.send("handle_#{option}".to_sym, 'arg') + end + end + + + describe "in preinit" do + it "should set options[:parameteers] to true" do + @describe.preinit + + @describe.options[:parameters].should be_true + end + end + + describe "when handling parameters" do + it "should set options[:parameters] to false" do + @describe.handle_short(nil) + + @describe.options[:parameters].should be_false + end + end + + describe "during setup" do + it "should collect arguments in options[:types]" do + @describe.command_line.stubs(:args).returns(['1','2']) + @describe.setup + + @describe.options[:types].should == ['1','2'] + end + end + + describe "when running" do + + before :each do + @typedoc = stub 'type_doc' + TypeDoc.stubs(:new).returns(@typedoc) + end + + it "should call list_types if options list is set" do + @describe.options[:list] = true + + @typedoc.expects(:list_types) + + @describe.run_command + end + + it "should call format_type for each given types" do + @describe.options[:list] = false + @describe.options[:types] = ['type'] + + @typedoc.expects(:format_type).with('type', @describe.options) + @describe.run_command + end + end +end diff --git a/spec/unit/application/describe_spec_spec.rb b/spec/unit/application/describe_spec_spec.rb deleted file mode 100755 index 002cdb763..000000000 --- a/spec/unit/application/describe_spec_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/application/describe' - -describe Puppet::Application::Describe do - before :each do - @describe = Puppet::Application[:describe] - end - - it "should ask Puppet::Application to not parse Puppet configuration file" do - @describe.should_parse_config?.should be_false - end - - it "should declare a main command" do - @describe.should respond_to(:main) - end - - it "should declare a preinit block" do - @describe.should respond_to(:preinit) - end - - [:providers,:list,:meta].each do |option| - it "should declare handle_#{option} method" do - @describe.should respond_to("handle_#{option}".to_sym) - end - - it "should store argument value when calling handle_#{option}" do - @describe.options.expects(:[]=).with("#{option}".to_sym, 'arg') - @describe.send("handle_#{option}".to_sym, 'arg') - end - end - - - describe "in preinit" do - it "should set options[:parameteers] to true" do - @describe.preinit - - @describe.options[:parameters].should be_true - end - end - - describe "when handling parameters" do - it "should set options[:parameters] to false" do - @describe.handle_short(nil) - - @describe.options[:parameters].should be_false - end - end - - describe "during setup" do - it "should collect arguments in options[:types]" do - @describe.command_line.stubs(:args).returns(['1','2']) - @describe.setup - - @describe.options[:types].should == ['1','2'] - end - end - - describe "when running" do - - before :each do - @typedoc = stub 'type_doc' - TypeDoc.stubs(:new).returns(@typedoc) - end - - it "should call list_types if options list is set" do - @describe.options[:list] = true - - @typedoc.expects(:list_types) - - @describe.run_command - end - - it "should call format_type for each given types" do - @describe.options[:list] = false - @describe.options[:types] = ['type'] - - @typedoc.expects(:format_type).with('type', @describe.options) - @describe.run_command - end - end -end diff --git a/spec/unit/application/doc_spec.rb b/spec/unit/application/doc_spec.rb new file mode 100755 index 000000000..db805fe7e --- /dev/null +++ b/spec/unit/application/doc_spec.rb @@ -0,0 +1,368 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/application/doc' +require 'puppet/util/reference' +require 'puppet/util/rdoc' + +describe Puppet::Application::Doc do + before :each do + @doc = Puppet::Application[:doc] + @doc.stubs(:puts) + @doc.preinit + Puppet::Util::Log.stubs(:newdestination) + Puppet::Util::Log.stubs(:level=) + end + + it "should ask Puppet::Application to not parse Puppet configuration file" do + @doc.should_parse_config?.should be_false + end + + it "should declare a other command" do + @doc.should respond_to(:other) + end + + it "should declare a rdoc command" do + @doc.should respond_to(:rdoc) + end + + it "should declare a trac command" do + @doc.should respond_to(:trac) + end + + it "should declare a fallback for unknown options" do + @doc.should respond_to(:handle_unknown) + end + + it "should declare a preinit block" do + @doc.should respond_to(:preinit) + end + + describe "in preinit" do + it "should set references to []" do + @doc.preinit + + @doc.options[:references].should == [] + end + + it "should init mode to text" do + @doc.preinit + + @doc.options[:mode].should == :text + end + + it "should init format to to_rest" do + @doc.preinit + + @doc.options[:format].should == :to_rest + end + end + + describe "when handling options" do + [:all, :outputdir, :verbose, :debug, :charset].each do |option| + it "should declare handle_#{option} method" do + @doc.should respond_to("handle_#{option}".to_sym) + end + + it "should store argument value when calling handle_#{option}" do + @doc.options.expects(:[]=).with(option, 'arg') + @doc.send("handle_#{option}".to_sym, 'arg') + end + end + + it "should store the format if valid" do + Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(true) + + @doc.options.expects(:[]=).with(:format, 'to_format') + + @doc.handle_format('format') + end + + it "should raise an error if the format is not valid" do + Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(false) + lambda { @doc.handle_format('format') } + end + + it "should store the mode if valid" do + Puppet::Util::Reference.stubs(:modes).returns(stub('mode', :include? => true)) + + @doc.options.expects(:[]=).with(:mode, :mode) + + @doc.handle_mode('mode') + end + + it "should store the mode if :rdoc" do + Puppet::Util::Reference.modes.stubs(:include?).with('rdoc').returns(false) + + @doc.options.expects(:[]=).with(:mode, :rdoc) + + @doc.handle_mode('rdoc') + end + + it "should raise an error if the mode is not valid" do + Puppet::Util::Reference.modes.stubs(:include?).with('unknown').returns(false) + lambda { @doc.handle_mode('unknown') } + end + + it "should list all references on list and exit" do + reference = stubs 'reference' + ref = stubs 'ref' + Puppet::Util::Reference.stubs(:references).returns([reference]) + + Puppet::Util::Reference.expects(:reference).with(reference).returns(ref) + ref.expects(:doc) + @doc.expects(:exit) + + @doc.handle_list(nil) + end + + it "should add reference to references list with --reference" do + @doc.options[:references] = [:ref1] + + @doc.handle_reference('ref2') + + @doc.options[:references].should == [:ref1,:ref2] + end + end + + describe "during setup" do + + before :each do + Puppet::Log.stubs(:newdestination) + @doc.command_line.stubs(:args).returns([]) + end + + it "should default to rdoc mode if there are command line arguments" do + @doc.command_line.stubs(:args).returns(["1"]) + @doc.stubs(:setup_rdoc) + + @doc.options.expects(:[]=).with(:mode,:rdoc) + + @doc.setup + end + + it "should call setup_rdoc in rdoc mode" do + @doc.options.stubs(:[]).with(:mode).returns(:rdoc) + + @doc.expects(:setup_rdoc) + + @doc.setup + end + + it "should call setup_reference if not rdoc" do + @doc.options.stubs(:[]).with(:mode).returns(:test) + + @doc.expects(:setup_reference) + + @doc.setup + end + + describe "in non-rdoc mode" do + + it "should get all non-dynamic reference if --all" do + @doc.options.stubs(:[]).with(:all).returns(true) + @doc.options.stubs(:[]).with(:references).returns([]) + static = stub 'static', :dynamic? => false + dynamic = stub 'dynamic', :dynamic? => true + Puppet::Util::Reference.stubs(:reference).with(:static).returns(static) + Puppet::Util::Reference.stubs(:reference).with(:dynamic).returns(dynamic) + Puppet::Util::Reference.stubs(:references).returns([:static,:dynamic]) + + @doc.options.stubs(:[]=).with(:references, [:static]) + + @doc.setup_reference + end + + it "should default to :type if no references" do + @doc.options.stubs(:[]).with(:all).returns(false) + array = stub 'array', :empty? => true + @doc.options.stubs(:[]).with(:references).returns(array) + + array.expects(:<<).with(:type) + + @doc.setup_reference + end + + end + + describe "in rdoc mode" do + + before :each do + @doc.options.stubs(:[]).returns(false) + Puppet.stubs(:parse_config) + Puppet::Util::Log.stubs(:level=) + Puppet::Util::Log.stubs(:newdestination) + end + + describe "when there are unknown args" do + + it "should expand --modulepath if any" do + @doc.unknown_args = [ { :opt => "--modulepath", :arg => "path" } ] + Puppet.settings.stubs(:handlearg) + + File.expects(:expand_path).with("path") + + @doc.setup_rdoc + end + + it "should expand --manifestdir if any" do + @doc.unknown_args = [ { :opt => "--manifestdir", :arg => "path" } ] + Puppet.settings.stubs(:handlearg) + + File.expects(:expand_path).with("path") + + @doc.setup_rdoc + end + + it "should give them to Puppet.settings" do + @doc.unknown_args = [ { :opt => :option, :arg => :argument } ] + Puppet.settings.expects(:handlearg).with(:option,:argument) + + @doc.setup_rdoc + end + end + + it "should operate in master mode" do + @doc.class.mode.name.should == :master + + @doc.setup_rdoc + end + + it "should parse puppet configuration" do + Puppet.expects(:parse_config) + + @doc.setup_rdoc + end + + it "should set log level to debug if --debug" do + @doc.options.stubs(:[]).with(:debug).returns(true) + Puppet::Util::Log.expects(:level=).with(:debug) + + @doc.setup_rdoc + end + + it "should set log level to info if --verbose" do + @doc.options.stubs(:[]).with(:verbose).returns(true) + Puppet::Util::Log.expects(:level=).with(:info) + + @doc.setup_rdoc + end + + it "should set log destination to console if --verbose" do + @doc.options.stubs(:[]).with(:verbose).returns(true) + + Puppet::Util::Log.expects(:newdestination).with(:console) + + @doc.setup_rdoc + end + + it "should set log destination to console if --debug" do + @doc.options.stubs(:[]).with(:debug).returns(true) + + Puppet::Util::Log.expects(:newdestination).with(:console) + + @doc.setup_rdoc + end + + end + + end + + describe "when running" do + before :each do + end + + describe "in trac mode" do + it "should call trac for each reference" do + ref = stub 'ref' + Puppet::Util::Reference.stubs(:reference).with(:ref).returns(ref) + @doc.options.stubs(:[]).with(:references).returns([:ref]) + @doc.options.stubs(:[]).with(:mode).returns(:trac) + + ref.expects(:trac) + + @doc.trac + end + end + + describe "in rdoc mode" do + before :each do + @doc.manifest = false + Puppet.stubs(:info) + Puppet.stubs(:[]).with(:trace).returns(false) + @env = stub 'env' + Puppet::Node::Environment.stubs(:new).returns(@env) + @env.stubs(:modulepath).returns(['modules']) + @env.stubs(:[]).with(:manifest).returns('manifests/site.pp') + Puppet.stubs(:[]).with(:modulepath).returns('modules') + Puppet.stubs(:[]).with(:manifestdir).returns('manifests') + @doc.options.stubs(:[]).with(:all).returns(false) + @doc.options.stubs(:[]).with(:outputdir).returns('doc') + @doc.options.stubs(:[]).with(:charset).returns(nil) + Puppet.settings.stubs(:[]=).with(:document_all, false) + Puppet.settings.stubs(:setdefaults) + Puppet::Util::RDoc.stubs(:rdoc) + @doc.stubs(:exit) + File.stubs(:expand_path).with('modules').returns('modules') + File.stubs(:expand_path).with('manifests').returns('manifests') + @doc.command_line.stubs(:args).returns([]) + end + + it "should set document_all on --all" do + @doc.options.expects(:[]).with(:all).returns(true) + Puppet.settings.expects(:[]=).with(:document_all, true) + + @doc.rdoc + end + + it "should call Puppet::Util::RDoc.rdoc in full mode" do + Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], nil) + @doc.rdoc + end + + it "should call Puppet::Util::RDoc.rdoc with a charset if --charset has been provided" do + @doc.options.expects(:[]).with(:charset).returns("utf-8") + Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], "utf-8") + @doc.rdoc + end + + it "should call Puppet::Util::RDoc.rdoc in full mode with outputdir set to doc if no --outputdir" do + @doc.options.expects(:[]).with(:outputdir).returns(false) + Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], nil) + @doc.rdoc + end + + it "should call Puppet::Util::RDoc.manifestdoc in manifest mode" do + @doc.manifest = true + Puppet::Util::RDoc.expects(:manifestdoc) + @doc.rdoc + end + + it "should get modulepath and manifestdir values from the environment" do + @env.expects(:modulepath).returns(['envmodules1','envmodules2']) + @env.expects(:[]).with(:manifest).returns('envmanifests/site.pp') + + Puppet::Util::RDoc.expects(:rdoc).with('doc', ['envmodules1','envmodules2','envmanifests'], nil) + + @doc.rdoc + end + end + + describe "in the other modes" do + it "should get reference in given format" do + reference = stub 'reference' + @doc.options.stubs(:[]).with(:mode).returns(:none) + @doc.options.stubs(:[]).with(:references).returns([:ref]) + require 'puppet/util/reference' + Puppet::Util::Reference.expects(:reference).with(:ref).returns(reference) + @doc.options.stubs(:[]).with(:format).returns(:format) + @doc.stubs(:exit) + + reference.expects(:send).with { |format,contents| format == :format }.returns('doc') + @doc.other + end + end + + end +end diff --git a/spec/unit/application/doc_spec_spec.rb b/spec/unit/application/doc_spec_spec.rb deleted file mode 100755 index db805fe7e..000000000 --- a/spec/unit/application/doc_spec_spec.rb +++ /dev/null @@ -1,368 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/application/doc' -require 'puppet/util/reference' -require 'puppet/util/rdoc' - -describe Puppet::Application::Doc do - before :each do - @doc = Puppet::Application[:doc] - @doc.stubs(:puts) - @doc.preinit - Puppet::Util::Log.stubs(:newdestination) - Puppet::Util::Log.stubs(:level=) - end - - it "should ask Puppet::Application to not parse Puppet configuration file" do - @doc.should_parse_config?.should be_false - end - - it "should declare a other command" do - @doc.should respond_to(:other) - end - - it "should declare a rdoc command" do - @doc.should respond_to(:rdoc) - end - - it "should declare a trac command" do - @doc.should respond_to(:trac) - end - - it "should declare a fallback for unknown options" do - @doc.should respond_to(:handle_unknown) - end - - it "should declare a preinit block" do - @doc.should respond_to(:preinit) - end - - describe "in preinit" do - it "should set references to []" do - @doc.preinit - - @doc.options[:references].should == [] - end - - it "should init mode to text" do - @doc.preinit - - @doc.options[:mode].should == :text - end - - it "should init format to to_rest" do - @doc.preinit - - @doc.options[:format].should == :to_rest - end - end - - describe "when handling options" do - [:all, :outputdir, :verbose, :debug, :charset].each do |option| - it "should declare handle_#{option} method" do - @doc.should respond_to("handle_#{option}".to_sym) - end - - it "should store argument value when calling handle_#{option}" do - @doc.options.expects(:[]=).with(option, 'arg') - @doc.send("handle_#{option}".to_sym, 'arg') - end - end - - it "should store the format if valid" do - Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(true) - - @doc.options.expects(:[]=).with(:format, 'to_format') - - @doc.handle_format('format') - end - - it "should raise an error if the format is not valid" do - Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(false) - lambda { @doc.handle_format('format') } - end - - it "should store the mode if valid" do - Puppet::Util::Reference.stubs(:modes).returns(stub('mode', :include? => true)) - - @doc.options.expects(:[]=).with(:mode, :mode) - - @doc.handle_mode('mode') - end - - it "should store the mode if :rdoc" do - Puppet::Util::Reference.modes.stubs(:include?).with('rdoc').returns(false) - - @doc.options.expects(:[]=).with(:mode, :rdoc) - - @doc.handle_mode('rdoc') - end - - it "should raise an error if the mode is not valid" do - Puppet::Util::Reference.modes.stubs(:include?).with('unknown').returns(false) - lambda { @doc.handle_mode('unknown') } - end - - it "should list all references on list and exit" do - reference = stubs 'reference' - ref = stubs 'ref' - Puppet::Util::Reference.stubs(:references).returns([reference]) - - Puppet::Util::Reference.expects(:reference).with(reference).returns(ref) - ref.expects(:doc) - @doc.expects(:exit) - - @doc.handle_list(nil) - end - - it "should add reference to references list with --reference" do - @doc.options[:references] = [:ref1] - - @doc.handle_reference('ref2') - - @doc.options[:references].should == [:ref1,:ref2] - end - end - - describe "during setup" do - - before :each do - Puppet::Log.stubs(:newdestination) - @doc.command_line.stubs(:args).returns([]) - end - - it "should default to rdoc mode if there are command line arguments" do - @doc.command_line.stubs(:args).returns(["1"]) - @doc.stubs(:setup_rdoc) - - @doc.options.expects(:[]=).with(:mode,:rdoc) - - @doc.setup - end - - it "should call setup_rdoc in rdoc mode" do - @doc.options.stubs(:[]).with(:mode).returns(:rdoc) - - @doc.expects(:setup_rdoc) - - @doc.setup - end - - it "should call setup_reference if not rdoc" do - @doc.options.stubs(:[]).with(:mode).returns(:test) - - @doc.expects(:setup_reference) - - @doc.setup - end - - describe "in non-rdoc mode" do - - it "should get all non-dynamic reference if --all" do - @doc.options.stubs(:[]).with(:all).returns(true) - @doc.options.stubs(:[]).with(:references).returns([]) - static = stub 'static', :dynamic? => false - dynamic = stub 'dynamic', :dynamic? => true - Puppet::Util::Reference.stubs(:reference).with(:static).returns(static) - Puppet::Util::Reference.stubs(:reference).with(:dynamic).returns(dynamic) - Puppet::Util::Reference.stubs(:references).returns([:static,:dynamic]) - - @doc.options.stubs(:[]=).with(:references, [:static]) - - @doc.setup_reference - end - - it "should default to :type if no references" do - @doc.options.stubs(:[]).with(:all).returns(false) - array = stub 'array', :empty? => true - @doc.options.stubs(:[]).with(:references).returns(array) - - array.expects(:<<).with(:type) - - @doc.setup_reference - end - - end - - describe "in rdoc mode" do - - before :each do - @doc.options.stubs(:[]).returns(false) - Puppet.stubs(:parse_config) - Puppet::Util::Log.stubs(:level=) - Puppet::Util::Log.stubs(:newdestination) - end - - describe "when there are unknown args" do - - it "should expand --modulepath if any" do - @doc.unknown_args = [ { :opt => "--modulepath", :arg => "path" } ] - Puppet.settings.stubs(:handlearg) - - File.expects(:expand_path).with("path") - - @doc.setup_rdoc - end - - it "should expand --manifestdir if any" do - @doc.unknown_args = [ { :opt => "--manifestdir", :arg => "path" } ] - Puppet.settings.stubs(:handlearg) - - File.expects(:expand_path).with("path") - - @doc.setup_rdoc - end - - it "should give them to Puppet.settings" do - @doc.unknown_args = [ { :opt => :option, :arg => :argument } ] - Puppet.settings.expects(:handlearg).with(:option,:argument) - - @doc.setup_rdoc - end - end - - it "should operate in master mode" do - @doc.class.mode.name.should == :master - - @doc.setup_rdoc - end - - it "should parse puppet configuration" do - Puppet.expects(:parse_config) - - @doc.setup_rdoc - end - - it "should set log level to debug if --debug" do - @doc.options.stubs(:[]).with(:debug).returns(true) - Puppet::Util::Log.expects(:level=).with(:debug) - - @doc.setup_rdoc - end - - it "should set log level to info if --verbose" do - @doc.options.stubs(:[]).with(:verbose).returns(true) - Puppet::Util::Log.expects(:level=).with(:info) - - @doc.setup_rdoc - end - - it "should set log destination to console if --verbose" do - @doc.options.stubs(:[]).with(:verbose).returns(true) - - Puppet::Util::Log.expects(:newdestination).with(:console) - - @doc.setup_rdoc - end - - it "should set log destination to console if --debug" do - @doc.options.stubs(:[]).with(:debug).returns(true) - - Puppet::Util::Log.expects(:newdestination).with(:console) - - @doc.setup_rdoc - end - - end - - end - - describe "when running" do - before :each do - end - - describe "in trac mode" do - it "should call trac for each reference" do - ref = stub 'ref' - Puppet::Util::Reference.stubs(:reference).with(:ref).returns(ref) - @doc.options.stubs(:[]).with(:references).returns([:ref]) - @doc.options.stubs(:[]).with(:mode).returns(:trac) - - ref.expects(:trac) - - @doc.trac - end - end - - describe "in rdoc mode" do - before :each do - @doc.manifest = false - Puppet.stubs(:info) - Puppet.stubs(:[]).with(:trace).returns(false) - @env = stub 'env' - Puppet::Node::Environment.stubs(:new).returns(@env) - @env.stubs(:modulepath).returns(['modules']) - @env.stubs(:[]).with(:manifest).returns('manifests/site.pp') - Puppet.stubs(:[]).with(:modulepath).returns('modules') - Puppet.stubs(:[]).with(:manifestdir).returns('manifests') - @doc.options.stubs(:[]).with(:all).returns(false) - @doc.options.stubs(:[]).with(:outputdir).returns('doc') - @doc.options.stubs(:[]).with(:charset).returns(nil) - Puppet.settings.stubs(:[]=).with(:document_all, false) - Puppet.settings.stubs(:setdefaults) - Puppet::Util::RDoc.stubs(:rdoc) - @doc.stubs(:exit) - File.stubs(:expand_path).with('modules').returns('modules') - File.stubs(:expand_path).with('manifests').returns('manifests') - @doc.command_line.stubs(:args).returns([]) - end - - it "should set document_all on --all" do - @doc.options.expects(:[]).with(:all).returns(true) - Puppet.settings.expects(:[]=).with(:document_all, true) - - @doc.rdoc - end - - it "should call Puppet::Util::RDoc.rdoc in full mode" do - Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], nil) - @doc.rdoc - end - - it "should call Puppet::Util::RDoc.rdoc with a charset if --charset has been provided" do - @doc.options.expects(:[]).with(:charset).returns("utf-8") - Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], "utf-8") - @doc.rdoc - end - - it "should call Puppet::Util::RDoc.rdoc in full mode with outputdir set to doc if no --outputdir" do - @doc.options.expects(:[]).with(:outputdir).returns(false) - Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], nil) - @doc.rdoc - end - - it "should call Puppet::Util::RDoc.manifestdoc in manifest mode" do - @doc.manifest = true - Puppet::Util::RDoc.expects(:manifestdoc) - @doc.rdoc - end - - it "should get modulepath and manifestdir values from the environment" do - @env.expects(:modulepath).returns(['envmodules1','envmodules2']) - @env.expects(:[]).with(:manifest).returns('envmanifests/site.pp') - - Puppet::Util::RDoc.expects(:rdoc).with('doc', ['envmodules1','envmodules2','envmanifests'], nil) - - @doc.rdoc - end - end - - describe "in the other modes" do - it "should get reference in given format" do - reference = stub 'reference' - @doc.options.stubs(:[]).with(:mode).returns(:none) - @doc.options.stubs(:[]).with(:references).returns([:ref]) - require 'puppet/util/reference' - Puppet::Util::Reference.expects(:reference).with(:ref).returns(reference) - @doc.options.stubs(:[]).with(:format).returns(:format) - @doc.stubs(:exit) - - reference.expects(:send).with { |format,contents| format == :format }.returns('doc') - @doc.other - end - end - - end -end diff --git a/spec/unit/application/filebucket_spec.rb b/spec/unit/application/filebucket_spec.rb new file mode 100644 index 000000000..58c7f7c75 --- /dev/null +++ b/spec/unit/application/filebucket_spec.rb @@ -0,0 +1,223 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/application/filebucket' + +describe Puppet::Application::Filebucket do + before :each do + @filebucket = Puppet::Application[:filebucket] + end + + it "should ask Puppet::Application to not parse Puppet configuration file" do + @filebucket.should_parse_config?.should be_false + end + + it "should declare a get command" do + @filebucket.should respond_to(:get) + end + + it "should declare a backup command" do + @filebucket.should respond_to(:backup) + end + + it "should declare a restore command" do + @filebucket.should respond_to(:restore) + end + + [:bucket, :debug, :local, :remote, :verbose].each do |option| + it "should declare handle_#{option} method" do + @filebucket.should respond_to("handle_#{option}".to_sym) + end + + it "should store argument value when calling handle_#{option}" do + @filebucket.options.expects(:[]=).with("#{option}".to_sym, 'arg') + @filebucket.send("handle_#{option}".to_sym, 'arg') + end + end + + describe "during setup" do + + before :each do + Puppet::Log.stubs(:newdestination) + Puppet.stubs(:settraps) + Puppet::Log.stubs(:level=) + Puppet.stubs(:parse_config) + Puppet::FileBucket::Dipper.stubs(:new) + @filebucket.options.stubs(:[]).with(any_parameters) + end + + + it "should set console as the log destination" do + Puppet::Log.expects(:newdestination).with(:console) + + @filebucket.setup + end + + it "should trap INT" do + @filebucket.expects(:trap).with(:INT) + + @filebucket.setup + end + + it "should set log level to debug if --debug was passed" do + @filebucket.options.stubs(:[]).with(:debug).returns(true) + + Puppet::Log.expects(:level=).with(:debug) + + @filebucket.setup + end + + it "should set log level to info if --verbose was passed" do + @filebucket.options.stubs(:[]).with(:verbose).returns(true) + + Puppet::Log.expects(:level=).with(:info) + + @filebucket.setup + end + + it "should Parse puppet config" do + Puppet.expects(:parse_config) + + @filebucket.setup + end + + it "should print puppet config if asked to in Puppet config" do + @filebucket.stubs(:exit) + Puppet.settings.stubs(:print_configs?).returns(true) + + Puppet.settings.expects(:print_configs) + + @filebucket.setup + end + + it "should exit after printing puppet config if asked to in Puppet config" do + Puppet.settings.stubs(:print_configs?).returns(true) + + lambda { @filebucket.setup }.should raise_error(SystemExit) + end + + describe "with local bucket" do + + before :each do + @filebucket.options.stubs(:[]).with(:local).returns(true) + end + + it "should create a client with the default bucket if none passed" do + Puppet.stubs(:[]).with(:bucketdir).returns("path") + + Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Path] == "path" } + + @filebucket.setup + end + + it "should create a local Dipper with the given bucket" do + @filebucket.options.stubs(:[]).with(:bucket).returns("path") + + Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Path] == "path" } + + @filebucket.setup + end + + end + + describe "with remote bucket" do + + it "should create a remote Client to the configured server" do + Puppet.stubs(:[]).with(:server).returns("puppet.reductivelabs.com") + + Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Server] == "puppet.reductivelabs.com" } + + @filebucket.setup + end + + end + + end + + describe "when running" do + + before :each do + Puppet::Log.stubs(:newdestination) + Puppet.stubs(:settraps) + Puppet::Log.stubs(:level=) + Puppet.stubs(:parse_config) + Puppet::FileBucket::Dipper.stubs(:new) + @filebucket.options.stubs(:[]).with(any_parameters) + + @client = stub 'client' + Puppet::FileBucket::Dipper.stubs(:new).returns(@client) + + @filebucket.setup + end + + it "should use the first non-option parameter as the dispatch" do + @filebucket.command_line.stubs(:args).returns(['get']) + + @filebucket.expects(:get) + + @filebucket.run_command + end + + describe "the command get" do + + before :each do + @filebucket.stubs(:print) + @filebucket.stubs(:args).returns([]) + end + + it "should call the client getfile method" do + @client.expects(:getfile) + + @filebucket.get + end + + it "should call the client getfile method with the given md5" do + md5="DEADBEEF" + @filebucket.stubs(:args).returns([md5]) + + @client.expects(:getfile).with(md5) + + @filebucket.get + end + + it "should print the file content" do + @client.stubs(:getfile).returns("content") + + @filebucket.expects(:print).returns("content") + + @filebucket.get + end + + end + + describe "the command backup" do + it "should call the client backup method for each given parameter" do + @filebucket.stubs(:puts) + FileTest.stubs(:exists?).returns(true) + FileTest.stubs(:readable?).returns(true) + @filebucket.stubs(:args).returns(["file1", "file2"]) + + @client.expects(:backup).with("file1") + @client.expects(:backup).with("file2") + + @filebucket.backup + end + end + + describe "the command restore" do + it "should call the client getfile method with the given md5" do + md5="DEADBEEF" + file="testfile" + @filebucket.stubs(:args).returns([file, md5]) + + @client.expects(:restore).with(file,md5) + + @filebucket.restore + end + end + + end + + +end diff --git a/spec/unit/application/filebucket_spec_spec.rb b/spec/unit/application/filebucket_spec_spec.rb deleted file mode 100644 index 58c7f7c75..000000000 --- a/spec/unit/application/filebucket_spec_spec.rb +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/application/filebucket' - -describe Puppet::Application::Filebucket do - before :each do - @filebucket = Puppet::Application[:filebucket] - end - - it "should ask Puppet::Application to not parse Puppet configuration file" do - @filebucket.should_parse_config?.should be_false - end - - it "should declare a get command" do - @filebucket.should respond_to(:get) - end - - it "should declare a backup command" do - @filebucket.should respond_to(:backup) - end - - it "should declare a restore command" do - @filebucket.should respond_to(:restore) - end - - [:bucket, :debug, :local, :remote, :verbose].each do |option| - it "should declare handle_#{option} method" do - @filebucket.should respond_to("handle_#{option}".to_sym) - end - - it "should store argument value when calling handle_#{option}" do - @filebucket.options.expects(:[]=).with("#{option}".to_sym, 'arg') - @filebucket.send("handle_#{option}".to_sym, 'arg') - end - end - - describe "during setup" do - - before :each do - Puppet::Log.stubs(:newdestination) - Puppet.stubs(:settraps) - Puppet::Log.stubs(:level=) - Puppet.stubs(:parse_config) - Puppet::FileBucket::Dipper.stubs(:new) - @filebucket.options.stubs(:[]).with(any_parameters) - end - - - it "should set console as the log destination" do - Puppet::Log.expects(:newdestination).with(:console) - - @filebucket.setup - end - - it "should trap INT" do - @filebucket.expects(:trap).with(:INT) - - @filebucket.setup - end - - it "should set log level to debug if --debug was passed" do - @filebucket.options.stubs(:[]).with(:debug).returns(true) - - Puppet::Log.expects(:level=).with(:debug) - - @filebucket.setup - end - - it "should set log level to info if --verbose was passed" do - @filebucket.options.stubs(:[]).with(:verbose).returns(true) - - Puppet::Log.expects(:level=).with(:info) - - @filebucket.setup - end - - it "should Parse puppet config" do - Puppet.expects(:parse_config) - - @filebucket.setup - end - - it "should print puppet config if asked to in Puppet config" do - @filebucket.stubs(:exit) - Puppet.settings.stubs(:print_configs?).returns(true) - - Puppet.settings.expects(:print_configs) - - @filebucket.setup - end - - it "should exit after printing puppet config if asked to in Puppet config" do - Puppet.settings.stubs(:print_configs?).returns(true) - - lambda { @filebucket.setup }.should raise_error(SystemExit) - end - - describe "with local bucket" do - - before :each do - @filebucket.options.stubs(:[]).with(:local).returns(true) - end - - it "should create a client with the default bucket if none passed" do - Puppet.stubs(:[]).with(:bucketdir).returns("path") - - Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Path] == "path" } - - @filebucket.setup - end - - it "should create a local Dipper with the given bucket" do - @filebucket.options.stubs(:[]).with(:bucket).returns("path") - - Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Path] == "path" } - - @filebucket.setup - end - - end - - describe "with remote bucket" do - - it "should create a remote Client to the configured server" do - Puppet.stubs(:[]).with(:server).returns("puppet.reductivelabs.com") - - Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Server] == "puppet.reductivelabs.com" } - - @filebucket.setup - end - - end - - end - - describe "when running" do - - before :each do - Puppet::Log.stubs(:newdestination) - Puppet.stubs(:settraps) - Puppet::Log.stubs(:level=) - Puppet.stubs(:parse_config) - Puppet::FileBucket::Dipper.stubs(:new) - @filebucket.options.stubs(:[]).with(any_parameters) - - @client = stub 'client' - Puppet::FileBucket::Dipper.stubs(:new).returns(@client) - - @filebucket.setup - end - - it "should use the first non-option parameter as the dispatch" do - @filebucket.command_line.stubs(:args).returns(['get']) - - @filebucket.expects(:get) - - @filebucket.run_command - end - - describe "the command get" do - - before :each do - @filebucket.stubs(:print) - @filebucket.stubs(:args).returns([]) - end - - it "should call the client getfile method" do - @client.expects(:getfile) - - @filebucket.get - end - - it "should call the client getfile method with the given md5" do - md5="DEADBEEF" - @filebucket.stubs(:args).returns([md5]) - - @client.expects(:getfile).with(md5) - - @filebucket.get - end - - it "should print the file content" do - @client.stubs(:getfile).returns("content") - - @filebucket.expects(:print).returns("content") - - @filebucket.get - end - - end - - describe "the command backup" do - it "should call the client backup method for each given parameter" do - @filebucket.stubs(:puts) - FileTest.stubs(:exists?).returns(true) - FileTest.stubs(:readable?).returns(true) - @filebucket.stubs(:args).returns(["file1", "file2"]) - - @client.expects(:backup).with("file1") - @client.expects(:backup).with("file2") - - @filebucket.backup - end - end - - describe "the command restore" do - it "should call the client getfile method with the given md5" do - md5="DEADBEEF" - file="testfile" - @filebucket.stubs(:args).returns([file, md5]) - - @client.expects(:restore).with(file,md5) - - @filebucket.restore - end - end - - end - - -end diff --git a/spec/unit/application/kick_spec.rb b/spec/unit/application/kick_spec.rb new file mode 100755 index 000000000..58e71b2a4 --- /dev/null +++ b/spec/unit/application/kick_spec.rb @@ -0,0 +1,306 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/ldap/connection' +require 'puppet/application/kick' + +describe Puppet::Application::Kick do + + confine "Kick's eventloops can only start on POSIX" => Puppet.features.posix? + + before :each do + Puppet::Util::Ldap::Connection.stubs(:new).returns(stub_everything) + @kick = Puppet::Application[:kick] + Puppet::Util::Log.stubs(:newdestination) + Puppet::Util::Log.stubs(:level=) + end + + it "should ask Puppet::Application to not parse Puppet configuration file" do + @kick.should_parse_config?.should be_false + end + + it "should declare a main command" do + @kick.should respond_to(:main) + end + + it "should declare a test command" do + @kick.should respond_to(:test) + end + + it "should declare a preinit block" do + @kick.should respond_to(:preinit) + end + + describe "during preinit" do + before :each do + @kick.stubs(:trap) + end + + it "should catch INT and TERM" do + @kick.stubs(:trap).with { |arg,block| arg == :INT or arg == :TERM } + + @kick.preinit + end + + it "should set parallel option to 1" do + @kick.preinit + + @kick.options[:parallel].should == 1 + end + + it "should set verbose by default" do + @kick.preinit + + @kick.options[:verbose].should be_true + end + + it "should set fqdn by default" do + @kick.preinit + + @kick.options[:fqdn].should be_true + end + + it "should set ignoreschedules to 'false'" do + @kick.preinit + + @kick.options[:ignoreschedules].should be_false + end + + it "should set foreground to 'false'" do + @kick.preinit + + @kick.options[:foreground].should be_false + end + end + + describe "when applying options" do + + before do + @kick.preinit + end + + [:all, :foreground, :debug, :ping, :test].each do |option| + it "should declare handle_#{option} method" do + @kick.should respond_to("handle_#{option}".to_sym) + end + + it "should store argument value when calling handle_#{option}" do + @kick.options.expects(:[]=).with(option, 'arg') + @kick.send("handle_#{option}".to_sym, 'arg') + end + end + + it "should add to the host list with the host option" do + @kick.handle_host('host') + + @kick.hosts.should == ['host'] + end + + it "should add to the tag list with the tag option" do + @kick.handle_tag('tag') + + @kick.tags.should == ['tag'] + end + + it "should add to the class list with the class option" do + @kick.handle_class('class') + + @kick.classes.should == ['class'] + end + end + + describe "during setup" do + + before :each do + @kick.classes = [] + @kick.tags = [] + @kick.hosts = [] + Puppet::Log.stubs(:level=) + @kick.stubs(:trap) + @kick.stubs(:puts) + Puppet.stubs(:parse_config) + + @kick.options.stubs(:[]).with(any_parameters) + end + + it "should set log level to debug if --debug was passed" do + @kick.options.stubs(:[]).with(:debug).returns(true) + + Puppet::Log.expects(:level=).with(:debug) + + @kick.setup + end + + it "should set log level to info if --verbose was passed" do + @kick.options.stubs(:[]).with(:verbose).returns(true) + + Puppet::Log.expects(:level=).with(:info) + + @kick.setup + end + + it "should Parse puppet config" do + Puppet.expects(:parse_config) + + @kick.setup + end + + describe "when using the ldap node terminus" do + before :each do + Puppet.stubs(:[]).with(:node_terminus).returns("ldap") + end + + it "should pass the fqdn option to search" do + @kick.options.stubs(:[]).with(:fqdn).returns(:something) + @kick.options.stubs(:[]).with(:all).returns(true) + @kick.stubs(:puts) + + Puppet::Node.expects(:search).with("whatever",:fqdn => :something).returns([]) + + @kick.setup + end + + it "should search for all nodes if --all" do + @kick.options.stubs(:[]).with(:all).returns(true) + @kick.stubs(:puts) + + Puppet::Node.expects(:search).with("whatever",:fqdn => nil).returns([]) + + @kick.setup + end + + it "should search for nodes including given classes" do + @kick.options.stubs(:[]).with(:all).returns(false) + @kick.stubs(:puts) + @kick.classes = ['class'] + + Puppet::Node.expects(:search).with("whatever", :class => "class", :fqdn => nil).returns([]) + + @kick.setup + end + end + + describe "when using regular nodes" do + it "should fail if some classes have been specified" do + $stderr.stubs(:puts) + @kick.classes = ['class'] + + @kick.expects(:exit).with(24) + + @kick.setup + end + end + end + + describe "when running" do + before :each do + @kick.stubs(:puts) + end + + it "should dispatch to test if --test is used" do + @kick.options.stubs(:[]).with(:test).returns(true) + + @kick.expects(:test) + @kick.run_command + end + + it "should dispatch to main if --test is not used" do + @kick.options.stubs(:[]).with(:test).returns(false) + + @kick.expects(:main) + @kick.run_command + end + + describe "the test command" do + it "should exit with exit code 0 " do + @kick.expects(:exit).with(0) + + @kick.test + end + end + + describe "the main command" do + before :each do + @kick.options.stubs(:[]).with(:parallel).returns(1) + @kick.options.stubs(:[]).with(:ping).returns(false) + @kick.options.stubs(:[]).with(:ignoreschedules).returns(false) + @kick.options.stubs(:[]).with(:foreground).returns(false) + @kick.options.stubs(:[]).with(:debug).returns(false) + @kick.stubs(:print) + @kick.stubs(:exit) + @kick.preinit + @kick.parse_options + @kick.setup + $stderr.stubs(:puts) + end + + it "should create as much childs as --parallel" do + @kick.options.stubs(:[]).with(:parallel).returns(3) + @kick.hosts = ['host1', 'host2', 'host3'] + @kick.stubs(:exit).raises(SystemExit) + Process.stubs(:wait).returns(1).then.returns(2).then.returns(3).then.raises(Errno::ECHILD) + + @kick.expects(:fork).times(3).returns(1).then.returns(2).then.returns(3) + + lambda { @kick.main }.should raise_error + end + + it "should delegate to run_for_host per host" do + @kick.hosts = ['host1', 'host2'] + @kick.stubs(:exit).raises(SystemExit) + @kick.stubs(:fork).returns(1).yields + Process.stubs(:wait).returns(1).then.raises(Errno::ECHILD) + + @kick.expects(:run_for_host).times(2) + + lambda { @kick.main }.should raise_error + end + + describe "during call of run_for_host" do + before do + require 'puppet/run' + options = { + :background => true, :ignoreschedules => false, :tags => [] + } + @kick.preinit + @agent_run = Puppet::Run.new( options.dup ) + @agent_run.stubs(:status).returns("success") + + Puppet::Run.indirection.expects(:terminus_class=).with( :rest ) + Puppet::Run.expects(:new).with( options ).returns(@agent_run) + end + + it "should call run on a Puppet::Run for the given host" do + @agent_run.expects(:save).with('https://host:8139/production/run/host').returns(@agent_run) + + @kick.run_for_host('host') + end + + it "should exit the child with 0 on success" do + @agent_run.stubs(:status).returns("success") + + @kick.expects(:exit).with(0) + + @kick.run_for_host('host') + end + + it "should exit the child with 3 on running" do + @agent_run.stubs(:status).returns("running") + + @kick.expects(:exit).with(3) + + @kick.run_for_host('host') + end + + it "should exit the child with 12 on unknown answer" do + @agent_run.stubs(:status).returns("whatever") + + @kick.expects(:exit).with(12) + + @kick.run_for_host('host') + end + end + end + end +end diff --git a/spec/unit/application/kick_spec_spec.rb b/spec/unit/application/kick_spec_spec.rb deleted file mode 100755 index 58e71b2a4..000000000 --- a/spec/unit/application/kick_spec_spec.rb +++ /dev/null @@ -1,306 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/util/ldap/connection' -require 'puppet/application/kick' - -describe Puppet::Application::Kick do - - confine "Kick's eventloops can only start on POSIX" => Puppet.features.posix? - - before :each do - Puppet::Util::Ldap::Connection.stubs(:new).returns(stub_everything) - @kick = Puppet::Application[:kick] - Puppet::Util::Log.stubs(:newdestination) - Puppet::Util::Log.stubs(:level=) - end - - it "should ask Puppet::Application to not parse Puppet configuration file" do - @kick.should_parse_config?.should be_false - end - - it "should declare a main command" do - @kick.should respond_to(:main) - end - - it "should declare a test command" do - @kick.should respond_to(:test) - end - - it "should declare a preinit block" do - @kick.should respond_to(:preinit) - end - - describe "during preinit" do - before :each do - @kick.stubs(:trap) - end - - it "should catch INT and TERM" do - @kick.stubs(:trap).with { |arg,block| arg == :INT or arg == :TERM } - - @kick.preinit - end - - it "should set parallel option to 1" do - @kick.preinit - - @kick.options[:parallel].should == 1 - end - - it "should set verbose by default" do - @kick.preinit - - @kick.options[:verbose].should be_true - end - - it "should set fqdn by default" do - @kick.preinit - - @kick.options[:fqdn].should be_true - end - - it "should set ignoreschedules to 'false'" do - @kick.preinit - - @kick.options[:ignoreschedules].should be_false - end - - it "should set foreground to 'false'" do - @kick.preinit - - @kick.options[:foreground].should be_false - end - end - - describe "when applying options" do - - before do - @kick.preinit - end - - [:all, :foreground, :debug, :ping, :test].each do |option| - it "should declare handle_#{option} method" do - @kick.should respond_to("handle_#{option}".to_sym) - end - - it "should store argument value when calling handle_#{option}" do - @kick.options.expects(:[]=).with(option, 'arg') - @kick.send("handle_#{option}".to_sym, 'arg') - end - end - - it "should add to the host list with the host option" do - @kick.handle_host('host') - - @kick.hosts.should == ['host'] - end - - it "should add to the tag list with the tag option" do - @kick.handle_tag('tag') - - @kick.tags.should == ['tag'] - end - - it "should add to the class list with the class option" do - @kick.handle_class('class') - - @kick.classes.should == ['class'] - end - end - - describe "during setup" do - - before :each do - @kick.classes = [] - @kick.tags = [] - @kick.hosts = [] - Puppet::Log.stubs(:level=) - @kick.stubs(:trap) - @kick.stubs(:puts) - Puppet.stubs(:parse_config) - - @kick.options.stubs(:[]).with(any_parameters) - end - - it "should set log level to debug if --debug was passed" do - @kick.options.stubs(:[]).with(:debug).returns(true) - - Puppet::Log.expects(:level=).with(:debug) - - @kick.setup - end - - it "should set log level to info if --verbose was passed" do - @kick.options.stubs(:[]).with(:verbose).returns(true) - - Puppet::Log.expects(:level=).with(:info) - - @kick.setup - end - - it "should Parse puppet config" do - Puppet.expects(:parse_config) - - @kick.setup - end - - describe "when using the ldap node terminus" do - before :each do - Puppet.stubs(:[]).with(:node_terminus).returns("ldap") - end - - it "should pass the fqdn option to search" do - @kick.options.stubs(:[]).with(:fqdn).returns(:something) - @kick.options.stubs(:[]).with(:all).returns(true) - @kick.stubs(:puts) - - Puppet::Node.expects(:search).with("whatever",:fqdn => :something).returns([]) - - @kick.setup - end - - it "should search for all nodes if --all" do - @kick.options.stubs(:[]).with(:all).returns(true) - @kick.stubs(:puts) - - Puppet::Node.expects(:search).with("whatever",:fqdn => nil).returns([]) - - @kick.setup - end - - it "should search for nodes including given classes" do - @kick.options.stubs(:[]).with(:all).returns(false) - @kick.stubs(:puts) - @kick.classes = ['class'] - - Puppet::Node.expects(:search).with("whatever", :class => "class", :fqdn => nil).returns([]) - - @kick.setup - end - end - - describe "when using regular nodes" do - it "should fail if some classes have been specified" do - $stderr.stubs(:puts) - @kick.classes = ['class'] - - @kick.expects(:exit).with(24) - - @kick.setup - end - end - end - - describe "when running" do - before :each do - @kick.stubs(:puts) - end - - it "should dispatch to test if --test is used" do - @kick.options.stubs(:[]).with(:test).returns(true) - - @kick.expects(:test) - @kick.run_command - end - - it "should dispatch to main if --test is not used" do - @kick.options.stubs(:[]).with(:test).returns(false) - - @kick.expects(:main) - @kick.run_command - end - - describe "the test command" do - it "should exit with exit code 0 " do - @kick.expects(:exit).with(0) - - @kick.test - end - end - - describe "the main command" do - before :each do - @kick.options.stubs(:[]).with(:parallel).returns(1) - @kick.options.stubs(:[]).with(:ping).returns(false) - @kick.options.stubs(:[]).with(:ignoreschedules).returns(false) - @kick.options.stubs(:[]).with(:foreground).returns(false) - @kick.options.stubs(:[]).with(:debug).returns(false) - @kick.stubs(:print) - @kick.stubs(:exit) - @kick.preinit - @kick.parse_options - @kick.setup - $stderr.stubs(:puts) - end - - it "should create as much childs as --parallel" do - @kick.options.stubs(:[]).with(:parallel).returns(3) - @kick.hosts = ['host1', 'host2', 'host3'] - @kick.stubs(:exit).raises(SystemExit) - Process.stubs(:wait).returns(1).then.returns(2).then.returns(3).then.raises(Errno::ECHILD) - - @kick.expects(:fork).times(3).returns(1).then.returns(2).then.returns(3) - - lambda { @kick.main }.should raise_error - end - - it "should delegate to run_for_host per host" do - @kick.hosts = ['host1', 'host2'] - @kick.stubs(:exit).raises(SystemExit) - @kick.stubs(:fork).returns(1).yields - Process.stubs(:wait).returns(1).then.raises(Errno::ECHILD) - - @kick.expects(:run_for_host).times(2) - - lambda { @kick.main }.should raise_error - end - - describe "during call of run_for_host" do - before do - require 'puppet/run' - options = { - :background => true, :ignoreschedules => false, :tags => [] - } - @kick.preinit - @agent_run = Puppet::Run.new( options.dup ) - @agent_run.stubs(:status).returns("success") - - Puppet::Run.indirection.expects(:terminus_class=).with( :rest ) - Puppet::Run.expects(:new).with( options ).returns(@agent_run) - end - - it "should call run on a Puppet::Run for the given host" do - @agent_run.expects(:save).with('https://host:8139/production/run/host').returns(@agent_run) - - @kick.run_for_host('host') - end - - it "should exit the child with 0 on success" do - @agent_run.stubs(:status).returns("success") - - @kick.expects(:exit).with(0) - - @kick.run_for_host('host') - end - - it "should exit the child with 3 on running" do - @agent_run.stubs(:status).returns("running") - - @kick.expects(:exit).with(3) - - @kick.run_for_host('host') - end - - it "should exit the child with 12 on unknown answer" do - @agent_run.stubs(:status).returns("whatever") - - @kick.expects(:exit).with(12) - - @kick.run_for_host('host') - end - end - end - end -end diff --git a/spec/unit/application/master_spec.rb b/spec/unit/application/master_spec.rb new file mode 100644 index 000000000..c3f4d9e37 --- /dev/null +++ b/spec/unit/application/master_spec.rb @@ -0,0 +1,447 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/application/master' + +describe Puppet::Application::Master do + before :each do + @master = Puppet::Application[:master] + @daemon = stub_everything 'daemon' + Puppet::Daemon.stubs(:new).returns(@daemon) + Puppet::Util::Log.stubs(:newdestination) + Puppet::Util::Log.stubs(:level=) + + Puppet::Node.stubs(:terminus_class=) + Puppet::Node.stubs(:cache_class=) + Puppet::Node::Facts.stubs(:terminus_class=) + Puppet::Node::Facts.stubs(:cache_class=) + Puppet::Transaction::Report.stubs(:terminus_class=) + Puppet::Resource::Catalog.stubs(:terminus_class=) + end + + it "should ask Puppet::Application to parse Puppet configuration file" do + @master.should_parse_config?.should be_true + end + + it "should declare a main command" do + @master.should respond_to(:main) + end + + it "should declare a parseonly command" do + @master.should respond_to(:parseonly) + end + + it "should declare a compile command" do + @master.should respond_to(:compile) + end + + it "should declare a preinit block" do + @master.should respond_to(:preinit) + end + + describe "during preinit" do + before :each do + @master.stubs(:trap) + end + + it "should catch INT" do + @master.stubs(:trap).with { |arg,block| arg == :INT } + + @master.preinit + end + + it "should create a Puppet Daemon" do + Puppet::Daemon.expects(:new).returns(@daemon) + + @master.preinit + end + + it "should give ARGV to the Daemon" do + argv = stub 'argv' + ARGV.stubs(:dup).returns(argv) + @daemon.expects(:argv=).with(argv) + + @master.preinit + end + + end + + [:debug,:verbose].each do |option| + it "should declare handle_#{option} method" do + @master.should respond_to("handle_#{option}".to_sym) + end + + it "should store argument value when calling handle_#{option}" do + @master.options.expects(:[]=).with(option, 'arg') + @master.send("handle_#{option}".to_sym, 'arg') + end + end + + describe "when applying options" do + before do + @master.command_line.stubs(:args).returns([]) + end + + it "should set the log destination with --logdest" do + Puppet::Log.expects(:newdestination).with("console") + + @master.handle_logdest("console") + end + + it "should put the setdest options to true" do + @master.options.expects(:[]=).with(:setdest,true) + + @master.handle_logdest("console") + end + + it "should parse the log destination from ARGV" do + @master.command_line.stubs(:args).returns(%w[--logdest /my/file]) + + Puppet::Util::Log.expects(:newdestination).with("/my/file") + + @master.parse_options + end + end + + describe "during setup" do + + before :each do + Puppet::Log.stubs(:newdestination) + Puppet.stubs(:settraps) + Puppet::Log.stubs(:level=) + Puppet::SSL::CertificateAuthority.stubs(:instance) + Puppet::SSL::CertificateAuthority.stubs(:ca?) + Puppet.settings.stubs(:use) + + @master.options.stubs(:[]).with(any_parameters) + end + + it "should set log level to debug if --debug was passed" do + @master.options.stubs(:[]).with(:debug).returns(true) + + Puppet::Log.expects(:level=).with(:debug) + + @master.setup + end + + it "should set log level to info if --verbose was passed" do + @master.options.stubs(:[]).with(:verbose).returns(true) + + Puppet::Log.expects(:level=).with(:info) + + @master.setup + end + + it "should set console as the log destination if no --logdest and --daemonize" do + @master.stubs(:[]).with(:daemonize).returns(:false) + + Puppet::Log.expects(:newdestination).with(:syslog) + + @master.setup + end + + it "should set syslog as the log destination if no --logdest and not --daemonize" do + Puppet::Log.expects(:newdestination).with(:syslog) + + @master.setup + end + + it "should set syslog as the log destination if --rack" do + @master.options.stubs(:[]).with(:rack).returns(:true) + + Puppet::Log.expects(:newdestination).with(:syslog) + + @master.setup + end + + it "should print puppet config if asked to in Puppet config" do + @master.stubs(:exit) + Puppet.settings.stubs(:print_configs?).returns(true) + + Puppet.settings.expects(:print_configs) + + @master.setup + end + + it "should exit after printing puppet config if asked to in Puppet config" do + Puppet.settings.stubs(:print_configs?).returns(true) + + lambda { @master.setup }.should raise_error(SystemExit) + end + + it "should tell Puppet.settings to use :main,:ssl and :master category" do + Puppet.settings.expects(:use).with(:main,:master,:ssl) + + @master.setup + end + + it "should cache class in yaml" do + Puppet::Node.expects(:cache_class=).with(:yaml) + + @master.setup + end + + describe "with no ca" do + + it "should set the ca_location to none" do + Puppet::SSL::Host.expects(:ca_location=).with(:none) + + @master.setup + end + + end + + describe "with a ca configured" do + + before :each do + Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) + end + + it "should set the ca_location to local" do + Puppet::SSL::Host.expects(:ca_location=).with(:local) + + @master.setup + end + + it "should tell Puppet.settings to use :ca category" do + Puppet.settings.expects(:use).with(:ca) + + @master.setup + end + + it "should instantiate the CertificateAuthority singleton" do + Puppet::SSL::CertificateAuthority.expects(:instance) + + @master.setup + end + + + end + + end + + describe "when running" do + before do + @master.preinit + end + + it "should dispatch to parseonly if parseonly is set" do + Puppet.stubs(:[]).with(:parseonly).returns(true) + @master.options[:node] = nil + + @master.expects(:parseonly) + @master.run_command + end + + it "should dispatch to compile if called with --compile" do + @master.options[:node] = "foo" + @master.expects(:compile) + @master.run_command + end + + it "should dispatch to main if parseonly is not set" do + Puppet.stubs(:[]).with(:parseonly).returns(false) + @master.options[:node] = nil + + @master.expects(:main) + @master.run_command + end + + + describe "the parseonly command" do + before :each do + Puppet.stubs(:[]).with(:environment) + Puppet.stubs(:[]).with(:manifest).returns("site.pp") + Puppet.stubs(:err) + @master.stubs(:exit) + @collection = stub_everything + Puppet::Resource::TypeCollection.stubs(:new).returns(@collection) + end + + it "should use a Puppet Resource Type Collection to parse the file" do + @collection.expects(:perform_initial_import) + @master.parseonly + end + + it "should exit with exit code 0 if no error" do + @master.expects(:exit).with(0) + @master.parseonly + end + + it "should exit with exit code 1 if error" do + @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) + @master.expects(:exit).with(1) + @master.parseonly + end + end + + describe "the compile command" do + before do + Puppet.stubs(:[]).with(:environment) + Puppet.stubs(:[]).with(:manifest).returns("site.pp") + Puppet.stubs(:err) + @master.stubs(:jj) + @master.stubs(:exit) + Puppet.features.stubs(:pson?).returns true + end + + it "should fail if pson isn't available" do + Puppet.features.expects(:pson?).returns false + lambda { @master.compile }.should raise_error + end + + it "should compile a catalog for the specified node" do + @master.options[:node] = "foo" + Puppet::Resource::Catalog.expects(:find).with("foo").returns Puppet::Resource::Catalog.new + $stdout.stubs(:puts) + + @master.compile + end + + it "should convert the catalog to a pure-resource catalog and use 'jj' to pretty-print the catalog" do + catalog = Puppet::Resource::Catalog.new + Puppet::Resource::Catalog.expects(:find).returns catalog + + catalog.expects(:to_resource).returns("rescat") + + @master.options[:node] = "foo" + @master.expects(:jj).with("rescat") + + @master.compile + end + + it "should exit with error code 30 if no catalog can be found" do + @master.options[:node] = "foo" + Puppet::Resource::Catalog.expects(:find).returns nil + @master.expects(:exit).with(30) + $stderr.expects(:puts) + + @master.compile + end + + it "should exit with error code 30 if there's a failure" do + @master.options[:node] = "foo" + Puppet::Resource::Catalog.expects(:find).raises ArgumentError + @master.expects(:exit).with(30) + $stderr.expects(:puts) + + @master.compile + end + end + + describe "the main command" do + before :each do + @master.preinit + @server = stub_everything 'server' + Puppet::Network::Server.stubs(:new).returns(@server) + @app = stub_everything 'app' + Puppet::SSL::Host.stubs(:localhost) + Puppet::SSL::CertificateAuthority.stubs(:ca?) + Process.stubs(:uid).returns(1000) + Puppet.stubs(:service) + Puppet.stubs(:[]) + Puppet.stubs(:notice) + Puppet.stubs(:start) + end + + it "should create a Server" do + Puppet::Network::Server.expects(:new) + + @master.main + end + + it "should give the server to the daemon" do + @daemon.expects(:server=).with(@server) + + @master.main + end + + it "should create the server with the right XMLRPC handlers" do + Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket]} + + @master.main + end + + it "should create the server with a :ca xmlrpc handler if needed" do + Puppet.stubs(:[]).with(:ca).returns(true) + Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers].include?(:CA) } + + @master.main + end + + it "should generate a SSL cert for localhost" do + Puppet::SSL::Host.expects(:localhost) + + @master.main + end + + it "should make sure to *only* hit the CA for data" do + Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) + + Puppet::SSL::Host.expects(:ca_location=).with(:only) + + @master.main + end + + it "should drop privileges if running as root" do + Puppet.features.stubs(:root?).returns true + + Puppet::Util.expects(:chuser) + + @master.main + end + + it "should daemonize if needed" do + Puppet.stubs(:[]).with(:daemonize).returns(true) + + @daemon.expects(:daemonize) + + @master.main + end + + it "should start the service" do + @daemon.expects(:start) + + @master.main + end + + describe "with --rack" do + confine "Rack is not available" => Puppet.features.rack? + + before do + require 'puppet/network/http/rack' + Puppet::Network::HTTP::Rack.stubs(:new).returns(@app) + end + + it "it should create the app with REST and XMLRPC support" do + @master.options.stubs(:[]).with(:rack).returns(:true) + + Puppet::Network::HTTP::Rack.expects(:new).with { |args| + args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket] and + args[:protocols] == [:rest, :xmlrpc] + } + + @master.main + end + + it "it should not start a daemon" do + @master.options.stubs(:[]).with(:rack).returns(:true) + + @daemon.expects(:start).never + + @master.main + end + + it "it should return the app" do + @master.options.stubs(:[]).with(:rack).returns(:true) + + app = @master.main + app.should equal(@app) + end + + end + + end + end +end diff --git a/spec/unit/application/master_spec_spec.rb b/spec/unit/application/master_spec_spec.rb deleted file mode 100644 index c3f4d9e37..000000000 --- a/spec/unit/application/master_spec_spec.rb +++ /dev/null @@ -1,447 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/application/master' - -describe Puppet::Application::Master do - before :each do - @master = Puppet::Application[:master] - @daemon = stub_everything 'daemon' - Puppet::Daemon.stubs(:new).returns(@daemon) - Puppet::Util::Log.stubs(:newdestination) - Puppet::Util::Log.stubs(:level=) - - Puppet::Node.stubs(:terminus_class=) - Puppet::Node.stubs(:cache_class=) - Puppet::Node::Facts.stubs(:terminus_class=) - Puppet::Node::Facts.stubs(:cache_class=) - Puppet::Transaction::Report.stubs(:terminus_class=) - Puppet::Resource::Catalog.stubs(:terminus_class=) - end - - it "should ask Puppet::Application to parse Puppet configuration file" do - @master.should_parse_config?.should be_true - end - - it "should declare a main command" do - @master.should respond_to(:main) - end - - it "should declare a parseonly command" do - @master.should respond_to(:parseonly) - end - - it "should declare a compile command" do - @master.should respond_to(:compile) - end - - it "should declare a preinit block" do - @master.should respond_to(:preinit) - end - - describe "during preinit" do - before :each do - @master.stubs(:trap) - end - - it "should catch INT" do - @master.stubs(:trap).with { |arg,block| arg == :INT } - - @master.preinit - end - - it "should create a Puppet Daemon" do - Puppet::Daemon.expects(:new).returns(@daemon) - - @master.preinit - end - - it "should give ARGV to the Daemon" do - argv = stub 'argv' - ARGV.stubs(:dup).returns(argv) - @daemon.expects(:argv=).with(argv) - - @master.preinit - end - - end - - [:debug,:verbose].each do |option| - it "should declare handle_#{option} method" do - @master.should respond_to("handle_#{option}".to_sym) - end - - it "should store argument value when calling handle_#{option}" do - @master.options.expects(:[]=).with(option, 'arg') - @master.send("handle_#{option}".to_sym, 'arg') - end - end - - describe "when applying options" do - before do - @master.command_line.stubs(:args).returns([]) - end - - it "should set the log destination with --logdest" do - Puppet::Log.expects(:newdestination).with("console") - - @master.handle_logdest("console") - end - - it "should put the setdest options to true" do - @master.options.expects(:[]=).with(:setdest,true) - - @master.handle_logdest("console") - end - - it "should parse the log destination from ARGV" do - @master.command_line.stubs(:args).returns(%w[--logdest /my/file]) - - Puppet::Util::Log.expects(:newdestination).with("/my/file") - - @master.parse_options - end - end - - describe "during setup" do - - before :each do - Puppet::Log.stubs(:newdestination) - Puppet.stubs(:settraps) - Puppet::Log.stubs(:level=) - Puppet::SSL::CertificateAuthority.stubs(:instance) - Puppet::SSL::CertificateAuthority.stubs(:ca?) - Puppet.settings.stubs(:use) - - @master.options.stubs(:[]).with(any_parameters) - end - - it "should set log level to debug if --debug was passed" do - @master.options.stubs(:[]).with(:debug).returns(true) - - Puppet::Log.expects(:level=).with(:debug) - - @master.setup - end - - it "should set log level to info if --verbose was passed" do - @master.options.stubs(:[]).with(:verbose).returns(true) - - Puppet::Log.expects(:level=).with(:info) - - @master.setup - end - - it "should set console as the log destination if no --logdest and --daemonize" do - @master.stubs(:[]).with(:daemonize).returns(:false) - - Puppet::Log.expects(:newdestination).with(:syslog) - - @master.setup - end - - it "should set syslog as the log destination if no --logdest and not --daemonize" do - Puppet::Log.expects(:newdestination).with(:syslog) - - @master.setup - end - - it "should set syslog as the log destination if --rack" do - @master.options.stubs(:[]).with(:rack).returns(:true) - - Puppet::Log.expects(:newdestination).with(:syslog) - - @master.setup - end - - it "should print puppet config if asked to in Puppet config" do - @master.stubs(:exit) - Puppet.settings.stubs(:print_configs?).returns(true) - - Puppet.settings.expects(:print_configs) - - @master.setup - end - - it "should exit after printing puppet config if asked to in Puppet config" do - Puppet.settings.stubs(:print_configs?).returns(true) - - lambda { @master.setup }.should raise_error(SystemExit) - end - - it "should tell Puppet.settings to use :main,:ssl and :master category" do - Puppet.settings.expects(:use).with(:main,:master,:ssl) - - @master.setup - end - - it "should cache class in yaml" do - Puppet::Node.expects(:cache_class=).with(:yaml) - - @master.setup - end - - describe "with no ca" do - - it "should set the ca_location to none" do - Puppet::SSL::Host.expects(:ca_location=).with(:none) - - @master.setup - end - - end - - describe "with a ca configured" do - - before :each do - Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) - end - - it "should set the ca_location to local" do - Puppet::SSL::Host.expects(:ca_location=).with(:local) - - @master.setup - end - - it "should tell Puppet.settings to use :ca category" do - Puppet.settings.expects(:use).with(:ca) - - @master.setup - end - - it "should instantiate the CertificateAuthority singleton" do - Puppet::SSL::CertificateAuthority.expects(:instance) - - @master.setup - end - - - end - - end - - describe "when running" do - before do - @master.preinit - end - - it "should dispatch to parseonly if parseonly is set" do - Puppet.stubs(:[]).with(:parseonly).returns(true) - @master.options[:node] = nil - - @master.expects(:parseonly) - @master.run_command - end - - it "should dispatch to compile if called with --compile" do - @master.options[:node] = "foo" - @master.expects(:compile) - @master.run_command - end - - it "should dispatch to main if parseonly is not set" do - Puppet.stubs(:[]).with(:parseonly).returns(false) - @master.options[:node] = nil - - @master.expects(:main) - @master.run_command - end - - - describe "the parseonly command" do - before :each do - Puppet.stubs(:[]).with(:environment) - Puppet.stubs(:[]).with(:manifest).returns("site.pp") - Puppet.stubs(:err) - @master.stubs(:exit) - @collection = stub_everything - Puppet::Resource::TypeCollection.stubs(:new).returns(@collection) - end - - it "should use a Puppet Resource Type Collection to parse the file" do - @collection.expects(:perform_initial_import) - @master.parseonly - end - - it "should exit with exit code 0 if no error" do - @master.expects(:exit).with(0) - @master.parseonly - end - - it "should exit with exit code 1 if error" do - @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) - @master.expects(:exit).with(1) - @master.parseonly - end - end - - describe "the compile command" do - before do - Puppet.stubs(:[]).with(:environment) - Puppet.stubs(:[]).with(:manifest).returns("site.pp") - Puppet.stubs(:err) - @master.stubs(:jj) - @master.stubs(:exit) - Puppet.features.stubs(:pson?).returns true - end - - it "should fail if pson isn't available" do - Puppet.features.expects(:pson?).returns false - lambda { @master.compile }.should raise_error - end - - it "should compile a catalog for the specified node" do - @master.options[:node] = "foo" - Puppet::Resource::Catalog.expects(:find).with("foo").returns Puppet::Resource::Catalog.new - $stdout.stubs(:puts) - - @master.compile - end - - it "should convert the catalog to a pure-resource catalog and use 'jj' to pretty-print the catalog" do - catalog = Puppet::Resource::Catalog.new - Puppet::Resource::Catalog.expects(:find).returns catalog - - catalog.expects(:to_resource).returns("rescat") - - @master.options[:node] = "foo" - @master.expects(:jj).with("rescat") - - @master.compile - end - - it "should exit with error code 30 if no catalog can be found" do - @master.options[:node] = "foo" - Puppet::Resource::Catalog.expects(:find).returns nil - @master.expects(:exit).with(30) - $stderr.expects(:puts) - - @master.compile - end - - it "should exit with error code 30 if there's a failure" do - @master.options[:node] = "foo" - Puppet::Resource::Catalog.expects(:find).raises ArgumentError - @master.expects(:exit).with(30) - $stderr.expects(:puts) - - @master.compile - end - end - - describe "the main command" do - before :each do - @master.preinit - @server = stub_everything 'server' - Puppet::Network::Server.stubs(:new).returns(@server) - @app = stub_everything 'app' - Puppet::SSL::Host.stubs(:localhost) - Puppet::SSL::CertificateAuthority.stubs(:ca?) - Process.stubs(:uid).returns(1000) - Puppet.stubs(:service) - Puppet.stubs(:[]) - Puppet.stubs(:notice) - Puppet.stubs(:start) - end - - it "should create a Server" do - Puppet::Network::Server.expects(:new) - - @master.main - end - - it "should give the server to the daemon" do - @daemon.expects(:server=).with(@server) - - @master.main - end - - it "should create the server with the right XMLRPC handlers" do - Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket]} - - @master.main - end - - it "should create the server with a :ca xmlrpc handler if needed" do - Puppet.stubs(:[]).with(:ca).returns(true) - Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers].include?(:CA) } - - @master.main - end - - it "should generate a SSL cert for localhost" do - Puppet::SSL::Host.expects(:localhost) - - @master.main - end - - it "should make sure to *only* hit the CA for data" do - Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) - - Puppet::SSL::Host.expects(:ca_location=).with(:only) - - @master.main - end - - it "should drop privileges if running as root" do - Puppet.features.stubs(:root?).returns true - - Puppet::Util.expects(:chuser) - - @master.main - end - - it "should daemonize if needed" do - Puppet.stubs(:[]).with(:daemonize).returns(true) - - @daemon.expects(:daemonize) - - @master.main - end - - it "should start the service" do - @daemon.expects(:start) - - @master.main - end - - describe "with --rack" do - confine "Rack is not available" => Puppet.features.rack? - - before do - require 'puppet/network/http/rack' - Puppet::Network::HTTP::Rack.stubs(:new).returns(@app) - end - - it "it should create the app with REST and XMLRPC support" do - @master.options.stubs(:[]).with(:rack).returns(:true) - - Puppet::Network::HTTP::Rack.expects(:new).with { |args| - args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket] and - args[:protocols] == [:rest, :xmlrpc] - } - - @master.main - end - - it "it should not start a daemon" do - @master.options.stubs(:[]).with(:rack).returns(:true) - - @daemon.expects(:start).never - - @master.main - end - - it "it should return the app" do - @master.options.stubs(:[]).with(:rack).returns(:true) - - app = @master.main - app.should equal(@app) - end - - end - - end - end -end diff --git a/spec/unit/application/queue_spec.rb b/spec/unit/application/queue_spec.rb new file mode 100755 index 000000000..9f5b6c28c --- /dev/null +++ b/spec/unit/application/queue_spec.rb @@ -0,0 +1,186 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/application/queue' + +describe Puppet::Application::Queue do + before :each do + @queue = Puppet::Application[:queue] + @queue.stubs(:puts) + @daemon = stub_everything 'daemon', :daemonize => nil + Puppet::Util::Log.stubs(:newdestination) + Puppet::Util::Log.stubs(:level=) + + Puppet::Resource::Catalog.stubs(:terminus_class=) + end + + it "should ask Puppet::Application to parse Puppet configuration file" do + @queue.should_parse_config?.should be_true + end + + it "should declare a main command" do + @queue.should respond_to(:main) + end + + it "should declare a preinit block" do + @queue.should respond_to(:preinit) + end + + describe "in preinit" do + before :each do + @queue.stubs(:trap) + end + + it "should catch INT" do + @queue.expects(:trap).with { |arg,block| arg == :INT } + + @queue.preinit + end + + it "should init :verbose to false" do + @queue.preinit + + @queue.options[:verbose].should be_false + end + + it "should init :debug to false" do + @queue.preinit + + @queue.options[:debug].should be_false + end + + it "should create a Daemon instance and copy ARGV to it" do + ARGV.expects(:dup).returns "eh" + daemon = mock("daemon") + daemon.expects(:argv=).with("eh") + Puppet::Daemon.expects(:new).returns daemon + @queue.preinit + end + end + + describe "when handling options" do + + [:debug, :verbose].each do |option| + it "should declare handle_#{option} method" do + @queue.should respond_to("handle_#{option}".to_sym) + end + + it "should store argument value when calling handle_#{option}" do + @queue.options.expects(:[]=).with(option, 'arg') + @queue.send("handle_#{option}".to_sym, 'arg') + end + end + end + + describe "during setup" do + before :each do + @queue.options.stubs(:[]) + @queue.daemon.stubs(:daemonize) + Puppet.stubs(:info) + Puppet.features.stubs(:stomp?).returns true + Puppet::Resource::Catalog.stubs(:terminus_class=) + Puppet.stubs(:settraps) + Puppet.settings.stubs(:print_config?) + Puppet.settings.stubs(:print_config) + end + + it "should fail if the stomp feature is missing" do + Puppet.features.expects(:stomp?).returns false + lambda { @queue.setup }.should raise_error(ArgumentError) + end + + it "should print puppet config if asked to in Puppet config" do + @queue.stubs(:exit) + Puppet.settings.stubs(:print_configs?).returns(true) + + Puppet.settings.expects(:print_configs) + + @queue.setup + end + + it "should exit after printing puppet config if asked to in Puppet config" do + Puppet.settings.stubs(:print_configs?).returns(true) + + lambda { @queue.setup }.should raise_error(SystemExit) + end + + it "should call setup_logs" do + @queue.expects(:setup_logs) + @queue.setup + end + + describe "when setting up logs" do + before :each do + Puppet::Util::Log.stubs(:newdestination) + end + + it "should set log level to debug if --debug was passed" do + @queue.options.stubs(:[]).with(:debug).returns(true) + + Puppet::Util::Log.expects(:level=).with(:debug) + + @queue.setup_logs + end + + it "should set log level to info if --verbose was passed" do + @queue.options.stubs(:[]).with(:verbose).returns(true) + + Puppet::Util::Log.expects(:level=).with(:info) + + @queue.setup_logs + end + + [:verbose, :debug].each do |level| + it "should set console as the log destination with level #{level}" do + @queue.options.stubs(:[]).with(level).returns(true) + + Puppet::Util::Log.expects(:newdestination).with(:console) + + @queue.setup_logs + end + end + end + + it "should configure the Catalog class to use ActiveRecord" do + Puppet::Resource::Catalog.expects(:terminus_class=).with(:active_record) + + @queue.setup + end + + it "should daemonize if needed" do + Puppet.expects(:[]).with(:daemonize).returns(true) + + @queue.daemon.expects(:daemonize) + + @queue.setup + end + end + + describe "when running" do + before :each do + @queue.stubs(:sleep_forever) + Puppet::Resource::Catalog::Queue.stubs(:subscribe) + Thread.list.each { |t| t.stubs(:join) } + end + + it "should subscribe to the queue" do + Puppet::Resource::Catalog::Queue.expects(:subscribe) + @queue.main + end + + it "should log and save each catalog passed by the queue" do + catalog = mock 'catalog', :name => 'eh' + catalog.expects(:save) + + Puppet::Resource::Catalog::Queue.expects(:subscribe).yields(catalog) + Puppet.expects(:notice).times(2) + @queue.main + end + + it "should join all of the running threads" do + Thread.list.each { |t| t.expects(:join) } + @queue.main + end + end +end diff --git a/spec/unit/application/queue_spec_spec.rb b/spec/unit/application/queue_spec_spec.rb deleted file mode 100755 index 9f5b6c28c..000000000 --- a/spec/unit/application/queue_spec_spec.rb +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/application/queue' - -describe Puppet::Application::Queue do - before :each do - @queue = Puppet::Application[:queue] - @queue.stubs(:puts) - @daemon = stub_everything 'daemon', :daemonize => nil - Puppet::Util::Log.stubs(:newdestination) - Puppet::Util::Log.stubs(:level=) - - Puppet::Resource::Catalog.stubs(:terminus_class=) - end - - it "should ask Puppet::Application to parse Puppet configuration file" do - @queue.should_parse_config?.should be_true - end - - it "should declare a main command" do - @queue.should respond_to(:main) - end - - it "should declare a preinit block" do - @queue.should respond_to(:preinit) - end - - describe "in preinit" do - before :each do - @queue.stubs(:trap) - end - - it "should catch INT" do - @queue.expects(:trap).with { |arg,block| arg == :INT } - - @queue.preinit - end - - it "should init :verbose to false" do - @queue.preinit - - @queue.options[:verbose].should be_false - end - - it "should init :debug to false" do - @queue.preinit - - @queue.options[:debug].should be_false - end - - it "should create a Daemon instance and copy ARGV to it" do - ARGV.expects(:dup).returns "eh" - daemon = mock("daemon") - daemon.expects(:argv=).with("eh") - Puppet::Daemon.expects(:new).returns daemon - @queue.preinit - end - end - - describe "when handling options" do - - [:debug, :verbose].each do |option| - it "should declare handle_#{option} method" do - @queue.should respond_to("handle_#{option}".to_sym) - end - - it "should store argument value when calling handle_#{option}" do - @queue.options.expects(:[]=).with(option, 'arg') - @queue.send("handle_#{option}".to_sym, 'arg') - end - end - end - - describe "during setup" do - before :each do - @queue.options.stubs(:[]) - @queue.daemon.stubs(:daemonize) - Puppet.stubs(:info) - Puppet.features.stubs(:stomp?).returns true - Puppet::Resource::Catalog.stubs(:terminus_class=) - Puppet.stubs(:settraps) - Puppet.settings.stubs(:print_config?) - Puppet.settings.stubs(:print_config) - end - - it "should fail if the stomp feature is missing" do - Puppet.features.expects(:stomp?).returns false - lambda { @queue.setup }.should raise_error(ArgumentError) - end - - it "should print puppet config if asked to in Puppet config" do - @queue.stubs(:exit) - Puppet.settings.stubs(:print_configs?).returns(true) - - Puppet.settings.expects(:print_configs) - - @queue.setup - end - - it "should exit after printing puppet config if asked to in Puppet config" do - Puppet.settings.stubs(:print_configs?).returns(true) - - lambda { @queue.setup }.should raise_error(SystemExit) - end - - it "should call setup_logs" do - @queue.expects(:setup_logs) - @queue.setup - end - - describe "when setting up logs" do - before :each do - Puppet::Util::Log.stubs(:newdestination) - end - - it "should set log level to debug if --debug was passed" do - @queue.options.stubs(:[]).with(:debug).returns(true) - - Puppet::Util::Log.expects(:level=).with(:debug) - - @queue.setup_logs - end - - it "should set log level to info if --verbose was passed" do - @queue.options.stubs(:[]).with(:verbose).returns(true) - - Puppet::Util::Log.expects(:level=).with(:info) - - @queue.setup_logs - end - - [:verbose, :debug].each do |level| - it "should set console as the log destination with level #{level}" do - @queue.options.stubs(:[]).with(level).returns(true) - - Puppet::Util::Log.expects(:newdestination).with(:console) - - @queue.setup_logs - end - end - end - - it "should configure the Catalog class to use ActiveRecord" do - Puppet::Resource::Catalog.expects(:terminus_class=).with(:active_record) - - @queue.setup - end - - it "should daemonize if needed" do - Puppet.expects(:[]).with(:daemonize).returns(true) - - @queue.daemon.expects(:daemonize) - - @queue.setup - end - end - - describe "when running" do - before :each do - @queue.stubs(:sleep_forever) - Puppet::Resource::Catalog::Queue.stubs(:subscribe) - Thread.list.each { |t| t.stubs(:join) } - end - - it "should subscribe to the queue" do - Puppet::Resource::Catalog::Queue.expects(:subscribe) - @queue.main - end - - it "should log and save each catalog passed by the queue" do - catalog = mock 'catalog', :name => 'eh' - catalog.expects(:save) - - Puppet::Resource::Catalog::Queue.expects(:subscribe).yields(catalog) - Puppet.expects(:notice).times(2) - @queue.main - end - - it "should join all of the running threads" do - Thread.list.each { |t| t.expects(:join) } - @queue.main - end - end -end diff --git a/spec/unit/application/resource_spec.rb b/spec/unit/application/resource_spec.rb new file mode 100755 index 000000000..3487b2c3c --- /dev/null +++ b/spec/unit/application/resource_spec.rb @@ -0,0 +1,233 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/application/resource' + +describe Puppet::Application::Resource do + before :each do + @resource = Puppet::Application[:resource] + Puppet::Util::Log.stubs(:newdestination) + Puppet::Util::Log.stubs(:level=) + Puppet::Resource.indirection.stubs(:terminus_class=) + end + + it "should ask Puppet::Application to not parse Puppet configuration file" do + @resource.should_parse_config?.should be_false + end + + it "should declare a main command" do + @resource.should respond_to(:main) + end + + it "should declare a host option" do + @resource.should respond_to(:handle_host) + end + + it "should declare a types option" do + @resource.should respond_to(:handle_types) + end + + it "should declare a param option" do + @resource.should respond_to(:handle_param) + end + + it "should declare a preinit block" do + @resource.should respond_to(:preinit) + end + + describe "in preinit" do + it "should set hosts to nil" do + @resource.preinit + + @resource.host.should be_nil + end + + it "should init extra_params to empty array" do + @resource.preinit + + @resource.extra_params.should == [] + end + + it "should load Facter facts" do + Facter.expects(:loadfacts).once + @resource.preinit + end + end + + describe "when handling options" do + + [:debug, :verbose, :edit].each do |option| + it "should declare handle_#{option} method" do + @resource.should respond_to("handle_#{option}".to_sym) + end + + it "should store argument value when calling handle_#{option}" do + @resource.options.expects(:[]=).with(option, 'arg') + @resource.send("handle_#{option}".to_sym, 'arg') + end + end + + it "should set options[:host] to given host" do + @resource.handle_host(:whatever) + + @resource.host.should == :whatever + end + + it "should load an display all types with types option" do + type1 = stub_everything 'type1', :name => :type1 + type2 = stub_everything 'type2', :name => :type2 + Puppet::Type.stubs(:loadall) + Puppet::Type.stubs(:eachtype).multiple_yields(type1,type2) + @resource.stubs(:exit) + + @resource.expects(:puts).with(['type1','type2']) + @resource.handle_types(nil) + end + + it "should add param to extra_params list" do + @resource.extra_params = [ :param1 ] + @resource.handle_param("whatever") + + @resource.extra_params.should == [ :param1, :whatever ] + end + end + + describe "during setup" do + before :each do + Puppet::Log.stubs(:newdestination) + Puppet::Log.stubs(:level=) + Puppet.stubs(:parse_config) + end + + + it "should set console as the log destination" do + Puppet::Log.expects(:newdestination).with(:console) + + @resource.setup + end + + it "should set log level to debug if --debug was passed" do + @resource.options.stubs(:[]).with(:debug).returns(true) + + Puppet::Log.expects(:level=).with(:debug) + + @resource.setup + end + + it "should set log level to info if --verbose was passed" do + @resource.options.stubs(:[]).with(:debug).returns(false) + @resource.options.stubs(:[]).with(:verbose).returns(true) + + Puppet::Log.expects(:level=).with(:info) + + @resource.setup + end + + it "should Parse puppet config" do + Puppet.expects(:parse_config) + + @resource.setup + end + end + + describe "when running" do + + before :each do + @type = stub_everything 'type', :properties => [] + @resource.command_line.stubs(:args).returns(['type']) + Puppet::Type.stubs(:type).returns(@type) + end + + it "should raise an error if no type is given" do + @resource.command_line.stubs(:args).returns([]) + lambda { @resource.main }.should raise_error + end + + it "should raise an error when editing a remote host" do + @resource.options.stubs(:[]).with(:edit).returns(true) + @resource.host = 'host' + + lambda { @resource.main }.should raise_error + end + + it "should raise an error if the type is not found" do + Puppet::Type.stubs(:type).returns(nil) + + lambda { @resource.main }.should raise_error + end + + describe "with a host" do + before :each do + @resource.stubs(:puts) + @resource.host = 'host' + + Puppet::Resource.stubs(:find ).never + Puppet::Resource.stubs(:search).never + Puppet::Resource.stubs(:save ).never + end + + it "should search for resources" do + @resource.command_line.stubs(:args).returns(['type']) + Puppet::Resource.expects(:search).with('https://host:8139/production/resources/type/', {}).returns([]) + @resource.main + end + + it "should describe the given resource" do + @resource.command_line.stubs(:args).returns(['type', 'name']) + x = stub_everything 'resource' + Puppet::Resource.expects(:find).with('https://host:8139/production/resources/type/name').returns(x) + @resource.main + end + + it "should add given parameters to the object" do + @resource.command_line.stubs(:args).returns(['type','name','param=temp']) + + res = stub "resource" + res.expects(:save).with('https://host:8139/production/resources/type/name').returns(res) + res.expects(:collect) + res.expects(:to_manifest) + Puppet::Resource.expects(:new).with('type', 'name', {'param' => 'temp'}).returns(res) + + @resource.main + end + + end + + describe "without a host" do + before :each do + @resource.stubs(:puts) + @resource.host = nil + + Puppet::Resource.stubs(:find ).never + Puppet::Resource.stubs(:search).never + Puppet::Resource.stubs(:save ).never + end + + it "should search for resources" do + Puppet::Resource.expects(:search).with('type/', {}).returns([]) + @resource.main + end + + it "should describe the given resource" do + @resource.command_line.stubs(:args).returns(['type','name']) + x = stub_everything 'resource' + Puppet::Resource.expects(:find).with('type/name').returns(x) + @resource.main + end + + it "should add given parameters to the object" do + @resource.command_line.stubs(:args).returns(['type','name','param=temp']) + + res = stub "resource" + res.expects(:save).with('type/name').returns(res) + res.expects(:collect) + res.expects(:to_manifest) + Puppet::Resource.expects(:new).with('type', 'name', {'param' => 'temp'}).returns(res) + + @resource.main + end + + end + end +end diff --git a/spec/unit/application/resource_spec_spec.rb b/spec/unit/application/resource_spec_spec.rb deleted file mode 100755 index 3487b2c3c..000000000 --- a/spec/unit/application/resource_spec_spec.rb +++ /dev/null @@ -1,233 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/application/resource' - -describe Puppet::Application::Resource do - before :each do - @resource = Puppet::Application[:resource] - Puppet::Util::Log.stubs(:newdestination) - Puppet::Util::Log.stubs(:level=) - Puppet::Resource.indirection.stubs(:terminus_class=) - end - - it "should ask Puppet::Application to not parse Puppet configuration file" do - @resource.should_parse_config?.should be_false - end - - it "should declare a main command" do - @resource.should respond_to(:main) - end - - it "should declare a host option" do - @resource.should respond_to(:handle_host) - end - - it "should declare a types option" do - @resource.should respond_to(:handle_types) - end - - it "should declare a param option" do - @resource.should respond_to(:handle_param) - end - - it "should declare a preinit block" do - @resource.should respond_to(:preinit) - end - - describe "in preinit" do - it "should set hosts to nil" do - @resource.preinit - - @resource.host.should be_nil - end - - it "should init extra_params to empty array" do - @resource.preinit - - @resource.extra_params.should == [] - end - - it "should load Facter facts" do - Facter.expects(:loadfacts).once - @resource.preinit - end - end - - describe "when handling options" do - - [:debug, :verbose, :edit].each do |option| - it "should declare handle_#{option} method" do - @resource.should respond_to("handle_#{option}".to_sym) - end - - it "should store argument value when calling handle_#{option}" do - @resource.options.expects(:[]=).with(option, 'arg') - @resource.send("handle_#{option}".to_sym, 'arg') - end - end - - it "should set options[:host] to given host" do - @resource.handle_host(:whatever) - - @resource.host.should == :whatever - end - - it "should load an display all types with types option" do - type1 = stub_everything 'type1', :name => :type1 - type2 = stub_everything 'type2', :name => :type2 - Puppet::Type.stubs(:loadall) - Puppet::Type.stubs(:eachtype).multiple_yields(type1,type2) - @resource.stubs(:exit) - - @resource.expects(:puts).with(['type1','type2']) - @resource.handle_types(nil) - end - - it "should add param to extra_params list" do - @resource.extra_params = [ :param1 ] - @resource.handle_param("whatever") - - @resource.extra_params.should == [ :param1, :whatever ] - end - end - - describe "during setup" do - before :each do - Puppet::Log.stubs(:newdestination) - Puppet::Log.stubs(:level=) - Puppet.stubs(:parse_config) - end - - - it "should set console as the log destination" do - Puppet::Log.expects(:newdestination).with(:console) - - @resource.setup - end - - it "should set log level to debug if --debug was passed" do - @resource.options.stubs(:[]).with(:debug).returns(true) - - Puppet::Log.expects(:level=).with(:debug) - - @resource.setup - end - - it "should set log level to info if --verbose was passed" do - @resource.options.stubs(:[]).with(:debug).returns(false) - @resource.options.stubs(:[]).with(:verbose).returns(true) - - Puppet::Log.expects(:level=).with(:info) - - @resource.setup - end - - it "should Parse puppet config" do - Puppet.expects(:parse_config) - - @resource.setup - end - end - - describe "when running" do - - before :each do - @type = stub_everything 'type', :properties => [] - @resource.command_line.stubs(:args).returns(['type']) - Puppet::Type.stubs(:type).returns(@type) - end - - it "should raise an error if no type is given" do - @resource.command_line.stubs(:args).returns([]) - lambda { @resource.main }.should raise_error - end - - it "should raise an error when editing a remote host" do - @resource.options.stubs(:[]).with(:edit).returns(true) - @resource.host = 'host' - - lambda { @resource.main }.should raise_error - end - - it "should raise an error if the type is not found" do - Puppet::Type.stubs(:type).returns(nil) - - lambda { @resource.main }.should raise_error - end - - describe "with a host" do - before :each do - @resource.stubs(:puts) - @resource.host = 'host' - - Puppet::Resource.stubs(:find ).never - Puppet::Resource.stubs(:search).never - Puppet::Resource.stubs(:save ).never - end - - it "should search for resources" do - @resource.command_line.stubs(:args).returns(['type']) - Puppet::Resource.expects(:search).with('https://host:8139/production/resources/type/', {}).returns([]) - @resource.main - end - - it "should describe the given resource" do - @resource.command_line.stubs(:args).returns(['type', 'name']) - x = stub_everything 'resource' - Puppet::Resource.expects(:find).with('https://host:8139/production/resources/type/name').returns(x) - @resource.main - end - - it "should add given parameters to the object" do - @resource.command_line.stubs(:args).returns(['type','name','param=temp']) - - res = stub "resource" - res.expects(:save).with('https://host:8139/production/resources/type/name').returns(res) - res.expects(:collect) - res.expects(:to_manifest) - Puppet::Resource.expects(:new).with('type', 'name', {'param' => 'temp'}).returns(res) - - @resource.main - end - - end - - describe "without a host" do - before :each do - @resource.stubs(:puts) - @resource.host = nil - - Puppet::Resource.stubs(:find ).never - Puppet::Resource.stubs(:search).never - Puppet::Resource.stubs(:save ).never - end - - it "should search for resources" do - Puppet::Resource.expects(:search).with('type/', {}).returns([]) - @resource.main - end - - it "should describe the given resource" do - @resource.command_line.stubs(:args).returns(['type','name']) - x = stub_everything 'resource' - Puppet::Resource.expects(:find).with('type/name').returns(x) - @resource.main - end - - it "should add given parameters to the object" do - @resource.command_line.stubs(:args).returns(['type','name','param=temp']) - - res = stub "resource" - res.expects(:save).with('type/name').returns(res) - res.expects(:collect) - res.expects(:to_manifest) - Puppet::Resource.expects(:new).with('type', 'name', {'param' => 'temp'}).returns(res) - - @resource.main - end - - end - end -end diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb new file mode 100755 index 000000000..9dc655d14 --- /dev/null +++ b/spec/unit/application_spec.rb @@ -0,0 +1,519 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'puppet/application' +require 'puppet' +require 'getoptlong' + +describe Puppet::Application do + + before do + @app = Class.new(Puppet::Application).new + + # avoid actually trying to parse any settings + Puppet.settings.stubs(:parse) + end + + it "should have a run entry-point" do + @app.should respond_to(:run) + end + + it "should have a read accessor to options" do + @app.should respond_to(:options) + end + + it "should include a default setup method" do + @app.should respond_to(:setup) + end + + it "should include a default preinit method" do + @app.should respond_to(:preinit) + end + + it "should include a default run_command method" do + @app.should respond_to(:run_command) + end + + it "should invoke main as the default" do + @app.expects( :main ) + @app.run_command + end + + describe 'when invoking clear!' do + before :each do + Puppet::Application.run_status = :stop_requested + Puppet::Application.clear! + end + + it 'should have nil run_status' do + Puppet::Application.run_status.should be_nil + end + + it 'should return false for restart_requested?' do + Puppet::Application.restart_requested?.should be_false + end + + it 'should return false for stop_requested?' do + Puppet::Application.stop_requested?.should be_false + end + + it 'should return false for interrupted?' do + Puppet::Application.interrupted?.should be_false + end + + it 'should return true for clear?' do + Puppet::Application.clear?.should be_true + end + end + + describe 'after invoking stop!' do + before :each do + Puppet::Application.run_status = nil + Puppet::Application.stop! + end + + after :each do + Puppet::Application.run_status = nil + end + + it 'should have run_status of :stop_requested' do + Puppet::Application.run_status.should == :stop_requested + end + + it 'should return true for stop_requested?' do + Puppet::Application.stop_requested?.should be_true + end + + it 'should return false for restart_requested?' do + Puppet::Application.restart_requested?.should be_false + end + + it 'should return true for interrupted?' do + Puppet::Application.interrupted?.should be_true + end + + it 'should return false for clear?' do + Puppet::Application.clear?.should be_false + end + end + + describe 'when invoking restart!' do + before :each do + Puppet::Application.run_status = nil + Puppet::Application.restart! + end + + after :each do + Puppet::Application.run_status = nil + end + + it 'should have run_status of :restart_requested' do + Puppet::Application.run_status.should == :restart_requested + end + + it 'should return true for restart_requested?' do + Puppet::Application.restart_requested?.should be_true + end + + it 'should return false for stop_requested?' do + Puppet::Application.stop_requested?.should be_false + end + + it 'should return true for interrupted?' do + Puppet::Application.interrupted?.should be_true + end + + it 'should return false for clear?' do + Puppet::Application.clear?.should be_false + end + end + + describe 'when performing a controlled_run' do + it 'should not execute block if not :clear?' do + Puppet::Application.run_status = :stop_requested + target = mock 'target' + target.expects(:some_method).never + Puppet::Application.controlled_run do + target.some_method + end + end + + it 'should execute block if :clear?' do + Puppet::Application.run_status = nil + target = mock 'target' + target.expects(:some_method).once + Puppet::Application.controlled_run do + target.some_method + end + end + + describe 'on POSIX systems' do + confine "HUP works only on POSIX systems" => Puppet.features.posix? + + it 'should signal process with HUP after block if restart requested during block execution' do + Puppet::Application.run_status = nil + target = mock 'target' + target.expects(:some_method).once + old_handler = trap('HUP') { target.some_method } + begin + Puppet::Application.controlled_run do + Puppet::Application.run_status = :restart_requested + end + ensure + trap('HUP', old_handler) + end + end + end + + after :each do + Puppet::Application.run_status = nil + end + end + + describe "when parsing command-line options" do + + before :each do + @app.command_line.stubs(:args).returns([]) + + Puppet.settings.stubs(:optparse_addargs).returns([]) + end + + it "should create a new option parser when needed" do + option_parser = stub "option parser" + option_parser.stubs(:on) + option_parser.stubs(:default_argv=) + OptionParser.expects(:new).returns(option_parser).once + @app.option_parser.should == option_parser + @app.option_parser.should == option_parser + end + + it "should pass the banner to the option parser" do + option_parser = stub "option parser" + option_parser.stubs(:on) + option_parser.stubs(:default_argv=) + @app.class.instance_eval do + banner "banner" + end + + OptionParser.expects(:new).with("banner").returns(option_parser) + + @app.option_parser + end + + it "should set the optionparser's args to the command line args" do + option_parser = stub "option parser" + option_parser.stubs(:on) + option_parser.expects(:default_argv=).with(%w{ fake args }) + @app.command_line.stubs(:args).returns(%w{ fake args }) + OptionParser.expects(:new).returns(option_parser) + @app.option_parser + end + + it "should get options from Puppet.settings.optparse_addargs" do + Puppet.settings.expects(:optparse_addargs).returns([]) + + @app.parse_options + end + + it "should add Puppet.settings options to OptionParser" do + Puppet.settings.stubs(:optparse_addargs).returns( [["--option","-o", "Funny Option"]]) + + @app.option_parser.expects(:on).with { |*arg| arg == ["--option","-o", "Funny Option"] } + + @app.parse_options + end + + it "should ask OptionParser to parse the command-line argument" do + @app.option_parser.expects(:parse!) + + @app.parse_options + end + + describe "when using --help" do + confine "rdoc" => Puppet.features.usage? + + it "should call RDoc::usage and exit" do + @app.expects(:exit) + RDoc.expects(:usage).returns(true) + + @app.handle_help(nil) + end + + end + + describe "when using --version" do + it "should declare a version option" do + @app.should respond_to(:handle_version) + end + + it "should exit after printing the version" do + @app.stubs(:puts) + + lambda { @app.handle_version(nil) }.should raise_error(SystemExit) + end + end + + describe "when dealing with an argument not declared directly by the application" do + it "should pass it to handle_unknown if this method exists" do + Puppet.settings.stubs(:optparse_addargs).returns([["--not-handled"]]) + @app.option_parser.stubs(:on).yields("value") + + @app.expects(:handle_unknown).with("--not-handled", "value").returns(true) + + @app.parse_options + end + + it "should pass it to Puppet.settings if handle_unknown says so" do + Puppet.settings.stubs(:optparse_addargs).returns([["--topuppet"]]) + @app.option_parser.stubs(:on).yields("value") + + @app.stubs(:handle_unknown).with("--topuppet", "value").returns(false) + + Puppet.settings.expects(:handlearg).with("--topuppet", "value") + @app.parse_options + end + + it "should pass it to Puppet.settings if there is no handle_unknown method" do + Puppet.settings.stubs(:optparse_addargs).returns([["--topuppet"]]) + @app.option_parser.stubs(:on).yields("value") + + @app.stubs(:respond_to?).returns(false) + + Puppet.settings.expects(:handlearg).with("--topuppet", "value") + @app.parse_options + end + + it "should transform boolean false value to string for Puppet.settings" do + Puppet.settings.expects(:handlearg).with("--option", "false") + @app.handlearg("--option", false) + end + + it "should transform boolean true value to string for Puppet.settings" do + Puppet.settings.expects(:handlearg).with("--option", "true") + @app.handlearg("--option", true) + end + + it "should transform boolean option to normal form for Puppet.settings" do + Puppet.settings.expects(:handlearg).with("--option", "true") + @app.handlearg("--[no-]option", true) + end + + it "should transform boolean option to no- form for Puppet.settings" do + Puppet.settings.expects(:handlearg).with("--no-option", "false") + @app.handlearg("--[no-]option", false) + end + + end + + it "should exit if OptionParser raises an error" do + $stderr.stubs(:puts) + @app.option_parser.stubs(:parse!).raises(OptionParser::ParseError.new("blah blah")) + + @app.expects(:exit) + + lambda { @app.parse_options }.should_not raise_error + end + + end + + describe "when calling default setup" do + + before :each do + @app.stubs(:should_parse_config?).returns(false) + @app.options.stubs(:[]) + end + + [ :debug, :verbose ].each do |level| + it "should honor option #{level}" do + @app.options.stubs(:[]).with(level).returns(true) + Puppet::Util::Log.stubs(:newdestination) + + Puppet::Util::Log.expects(:level=).with(level == :verbose ? :info : :debug) + + @app.setup + end + end + + it "should honor setdest option" do + @app.options.stubs(:[]).with(:setdest).returns(false) + + Puppet::Util::Log.expects(:newdestination).with(:syslog) + + @app.setup + end + + end + + describe "when running" do + + before :each do + @app.stubs(:preinit) + @app.stubs(:setup) + @app.stubs(:parse_options) + end + + it "should call preinit" do + @app.stubs(:run_command) + + @app.expects(:preinit) + + @app.run + end + + it "should call parse_options" do + @app.stubs(:run_command) + + @app.expects(:parse_options) + + @app.run + end + + it "should call run_command" do + + @app.expects(:run_command) + + @app.run + end + + it "should parse Puppet configuration if should_parse_config is called" do + @app.stubs(:run_command) + @app.class.should_parse_config + + Puppet.settings.expects(:parse) + + @app.run + end + + it "should not parse_option if should_not_parse_config is called" do + @app.stubs(:run_command) + @app.class.should_not_parse_config + + Puppet.settings.expects(:parse).never + + @app.run + end + + it "should parse Puppet configuration if needed" do + @app.stubs(:run_command) + @app.stubs(:should_parse_config?).returns(true) + + Puppet.settings.expects(:parse) + + @app.run + end + + it "should call run_command" do + @app.expects(:run_command) + + @app.run + end + + it "should call main as the default command" do + @app.expects(:main) + + @app.run + end + + it "should warn and exit if no command can be called" do + $stderr.expects(:puts) + @app.expects(:exit).with(1) + @app.run + end + + it "should raise an error if dispatch returns no command" do + @app.stubs(:get_command).returns(nil) + $stderr.expects(:puts) + @app.expects(:exit).with(1) + @app.run + end + + it "should raise an error if dispatch returns an invalid command" do + @app.stubs(:get_command).returns(:this_function_doesnt_exist) + $stderr.expects(:puts) + @app.expects(:exit).with(1) + @app.run + end + end + + describe "when metaprogramming" do + + describe "when calling option" do + it "should create a new method named after the option" do + @app.class.option("--test1","-t") do + end + + @app.should respond_to(:handle_test1) + end + + it "should transpose in option name any '-' into '_'" do + @app.class.option("--test-dashes-again","-t") do + end + + @app.should respond_to(:handle_test_dashes_again) + end + + it "should create a new method called handle_test2 with option(\"--[no-]test2\")" do + @app.class.option("--[no-]test2","-t") do + end + + @app.should respond_to(:handle_test2) + end + + describe "when a block is passed" do + it "should create a new method with it" do + @app.class.option("--[no-]test2","-t") do + raise "I can't believe it, it works!" + end + + lambda { @app.handle_test2 }.should raise_error + end + + it "should declare the option to OptionParser" do + OptionParser.any_instance.stubs(:on) + OptionParser.any_instance.expects(:on).with { |*arg| arg[0] == "--[no-]test3" } + + @app.class.option("--[no-]test3","-t") do + end + + @app.option_parser + end + + it "should pass a block that calls our defined method" do + OptionParser.any_instance.stubs(:on) + OptionParser.any_instance.stubs(:on).with('--test4','-t').yields(nil) + + @app.expects(:send).with(:handle_test4, nil) + + @app.class.option("--test4","-t") do + end + + @app.option_parser + end + end + + describe "when no block is given" do + it "should declare the option to OptionParser" do + OptionParser.any_instance.stubs(:on) + OptionParser.any_instance.expects(:on).with("--test4","-t") + + @app.class.option("--test4","-t") + + @app.option_parser + end + + it "should give to OptionParser a block that adds the the value to the options array" do + OptionParser.any_instance.stubs(:on) + OptionParser.any_instance.stubs(:on).with("--test4","-t").yields(nil) + + @app.options.expects(:[]=).with(:test4,nil) + + @app.class.option("--test4","-t") + + @app.option_parser + end + end + end + + end +end diff --git a/spec/unit/application_spec_spec.rb b/spec/unit/application_spec_spec.rb deleted file mode 100755 index 9dc655d14..000000000 --- a/spec/unit/application_spec_spec.rb +++ /dev/null @@ -1,519 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -require 'puppet/application' -require 'puppet' -require 'getoptlong' - -describe Puppet::Application do - - before do - @app = Class.new(Puppet::Application).new - - # avoid actually trying to parse any settings - Puppet.settings.stubs(:parse) - end - - it "should have a run entry-point" do - @app.should respond_to(:run) - end - - it "should have a read accessor to options" do - @app.should respond_to(:options) - end - - it "should include a default setup method" do - @app.should respond_to(:setup) - end - - it "should include a default preinit method" do - @app.should respond_to(:preinit) - end - - it "should include a default run_command method" do - @app.should respond_to(:run_command) - end - - it "should invoke main as the default" do - @app.expects( :main ) - @app.run_command - end - - describe 'when invoking clear!' do - before :each do - Puppet::Application.run_status = :stop_requested - Puppet::Application.clear! - end - - it 'should have nil run_status' do - Puppet::Application.run_status.should be_nil - end - - it 'should return false for restart_requested?' do - Puppet::Application.restart_requested?.should be_false - end - - it 'should return false for stop_requested?' do - Puppet::Application.stop_requested?.should be_false - end - - it 'should return false for interrupted?' do - Puppet::Application.interrupted?.should be_false - end - - it 'should return true for clear?' do - Puppet::Application.clear?.should be_true - end - end - - describe 'after invoking stop!' do - before :each do - Puppet::Application.run_status = nil - Puppet::Application.stop! - end - - after :each do - Puppet::Application.run_status = nil - end - - it 'should have run_status of :stop_requested' do - Puppet::Application.run_status.should == :stop_requested - end - - it 'should return true for stop_requested?' do - Puppet::Application.stop_requested?.should be_true - end - - it 'should return false for restart_requested?' do - Puppet::Application.restart_requested?.should be_false - end - - it 'should return true for interrupted?' do - Puppet::Application.interrupted?.should be_true - end - - it 'should return false for clear?' do - Puppet::Application.clear?.should be_false - end - end - - describe 'when invoking restart!' do - before :each do - Puppet::Application.run_status = nil - Puppet::Application.restart! - end - - after :each do - Puppet::Application.run_status = nil - end - - it 'should have run_status of :restart_requested' do - Puppet::Application.run_status.should == :restart_requested - end - - it 'should return true for restart_requested?' do - Puppet::Application.restart_requested?.should be_true - end - - it 'should return false for stop_requested?' do - Puppet::Application.stop_requested?.should be_false - end - - it 'should return true for interrupted?' do - Puppet::Application.interrupted?.should be_true - end - - it 'should return false for clear?' do - Puppet::Application.clear?.should be_false - end - end - - describe 'when performing a controlled_run' do - it 'should not execute block if not :clear?' do - Puppet::Application.run_status = :stop_requested - target = mock 'target' - target.expects(:some_method).never - Puppet::Application.controlled_run do - target.some_method - end - end - - it 'should execute block if :clear?' do - Puppet::Application.run_status = nil - target = mock 'target' - target.expects(:some_method).once - Puppet::Application.controlled_run do - target.some_method - end - end - - describe 'on POSIX systems' do - confine "HUP works only on POSIX systems" => Puppet.features.posix? - - it 'should signal process with HUP after block if restart requested during block execution' do - Puppet::Application.run_status = nil - target = mock 'target' - target.expects(:some_method).once - old_handler = trap('HUP') { target.some_method } - begin - Puppet::Application.controlled_run do - Puppet::Application.run_status = :restart_requested - end - ensure - trap('HUP', old_handler) - end - end - end - - after :each do - Puppet::Application.run_status = nil - end - end - - describe "when parsing command-line options" do - - before :each do - @app.command_line.stubs(:args).returns([]) - - Puppet.settings.stubs(:optparse_addargs).returns([]) - end - - it "should create a new option parser when needed" do - option_parser = stub "option parser" - option_parser.stubs(:on) - option_parser.stubs(:default_argv=) - OptionParser.expects(:new).returns(option_parser).once - @app.option_parser.should == option_parser - @app.option_parser.should == option_parser - end - - it "should pass the banner to the option parser" do - option_parser = stub "option parser" - option_parser.stubs(:on) - option_parser.stubs(:default_argv=) - @app.class.instance_eval do - banner "banner" - end - - OptionParser.expects(:new).with("banner").returns(option_parser) - - @app.option_parser - end - - it "should set the optionparser's args to the command line args" do - option_parser = stub "option parser" - option_parser.stubs(:on) - option_parser.expects(:default_argv=).with(%w{ fake args }) - @app.command_line.stubs(:args).returns(%w{ fake args }) - OptionParser.expects(:new).returns(option_parser) - @app.option_parser - end - - it "should get options from Puppet.settings.optparse_addargs" do - Puppet.settings.expects(:optparse_addargs).returns([]) - - @app.parse_options - end - - it "should add Puppet.settings options to OptionParser" do - Puppet.settings.stubs(:optparse_addargs).returns( [["--option","-o", "Funny Option"]]) - - @app.option_parser.expects(:on).with { |*arg| arg == ["--option","-o", "Funny Option"] } - - @app.parse_options - end - - it "should ask OptionParser to parse the command-line argument" do - @app.option_parser.expects(:parse!) - - @app.parse_options - end - - describe "when using --help" do - confine "rdoc" => Puppet.features.usage? - - it "should call RDoc::usage and exit" do - @app.expects(:exit) - RDoc.expects(:usage).returns(true) - - @app.handle_help(nil) - end - - end - - describe "when using --version" do - it "should declare a version option" do - @app.should respond_to(:handle_version) - end - - it "should exit after printing the version" do - @app.stubs(:puts) - - lambda { @app.handle_version(nil) }.should raise_error(SystemExit) - end - end - - describe "when dealing with an argument not declared directly by the application" do - it "should pass it to handle_unknown if this method exists" do - Puppet.settings.stubs(:optparse_addargs).returns([["--not-handled"]]) - @app.option_parser.stubs(:on).yields("value") - - @app.expects(:handle_unknown).with("--not-handled", "value").returns(true) - - @app.parse_options - end - - it "should pass it to Puppet.settings if handle_unknown says so" do - Puppet.settings.stubs(:optparse_addargs).returns([["--topuppet"]]) - @app.option_parser.stubs(:on).yields("value") - - @app.stubs(:handle_unknown).with("--topuppet", "value").returns(false) - - Puppet.settings.expects(:handlearg).with("--topuppet", "value") - @app.parse_options - end - - it "should pass it to Puppet.settings if there is no handle_unknown method" do - Puppet.settings.stubs(:optparse_addargs).returns([["--topuppet"]]) - @app.option_parser.stubs(:on).yields("value") - - @app.stubs(:respond_to?).returns(false) - - Puppet.settings.expects(:handlearg).with("--topuppet", "value") - @app.parse_options - end - - it "should transform boolean false value to string for Puppet.settings" do - Puppet.settings.expects(:handlearg).with("--option", "false") - @app.handlearg("--option", false) - end - - it "should transform boolean true value to string for Puppet.settings" do - Puppet.settings.expects(:handlearg).with("--option", "true") - @app.handlearg("--option", true) - end - - it "should transform boolean option to normal form for Puppet.settings" do - Puppet.settings.expects(:handlearg).with("--option", "true") - @app.handlearg("--[no-]option", true) - end - - it "should transform boolean option to no- form for Puppet.settings" do - Puppet.settings.expects(:handlearg).with("--no-option", "false") - @app.handlearg("--[no-]option", false) - end - - end - - it "should exit if OptionParser raises an error" do - $stderr.stubs(:puts) - @app.option_parser.stubs(:parse!).raises(OptionParser::ParseError.new("blah blah")) - - @app.expects(:exit) - - lambda { @app.parse_options }.should_not raise_error - end - - end - - describe "when calling default setup" do - - before :each do - @app.stubs(:should_parse_config?).returns(false) - @app.options.stubs(:[]) - end - - [ :debug, :verbose ].each do |level| - it "should honor option #{level}" do - @app.options.stubs(:[]).with(level).returns(true) - Puppet::Util::Log.stubs(:newdestination) - - Puppet::Util::Log.expects(:level=).with(level == :verbose ? :info : :debug) - - @app.setup - end - end - - it "should honor setdest option" do - @app.options.stubs(:[]).with(:setdest).returns(false) - - Puppet::Util::Log.expects(:newdestination).with(:syslog) - - @app.setup - end - - end - - describe "when running" do - - before :each do - @app.stubs(:preinit) - @app.stubs(:setup) - @app.stubs(:parse_options) - end - - it "should call preinit" do - @app.stubs(:run_command) - - @app.expects(:preinit) - - @app.run - end - - it "should call parse_options" do - @app.stubs(:run_command) - - @app.expects(:parse_options) - - @app.run - end - - it "should call run_command" do - - @app.expects(:run_command) - - @app.run - end - - it "should parse Puppet configuration if should_parse_config is called" do - @app.stubs(:run_command) - @app.class.should_parse_config - - Puppet.settings.expects(:parse) - - @app.run - end - - it "should not parse_option if should_not_parse_config is called" do - @app.stubs(:run_command) - @app.class.should_not_parse_config - - Puppet.settings.expects(:parse).never - - @app.run - end - - it "should parse Puppet configuration if needed" do - @app.stubs(:run_command) - @app.stubs(:should_parse_config?).returns(true) - - Puppet.settings.expects(:parse) - - @app.run - end - - it "should call run_command" do - @app.expects(:run_command) - - @app.run - end - - it "should call main as the default command" do - @app.expects(:main) - - @app.run - end - - it "should warn and exit if no command can be called" do - $stderr.expects(:puts) - @app.expects(:exit).with(1) - @app.run - end - - it "should raise an error if dispatch returns no command" do - @app.stubs(:get_command).returns(nil) - $stderr.expects(:puts) - @app.expects(:exit).with(1) - @app.run - end - - it "should raise an error if dispatch returns an invalid command" do - @app.stubs(:get_command).returns(:this_function_doesnt_exist) - $stderr.expects(:puts) - @app.expects(:exit).with(1) - @app.run - end - end - - describe "when metaprogramming" do - - describe "when calling option" do - it "should create a new method named after the option" do - @app.class.option("--test1","-t") do - end - - @app.should respond_to(:handle_test1) - end - - it "should transpose in option name any '-' into '_'" do - @app.class.option("--test-dashes-again","-t") do - end - - @app.should respond_to(:handle_test_dashes_again) - end - - it "should create a new method called handle_test2 with option(\"--[no-]test2\")" do - @app.class.option("--[no-]test2","-t") do - end - - @app.should respond_to(:handle_test2) - end - - describe "when a block is passed" do - it "should create a new method with it" do - @app.class.option("--[no-]test2","-t") do - raise "I can't believe it, it works!" - end - - lambda { @app.handle_test2 }.should raise_error - end - - it "should declare the option to OptionParser" do - OptionParser.any_instance.stubs(:on) - OptionParser.any_instance.expects(:on).with { |*arg| arg[0] == "--[no-]test3" } - - @app.class.option("--[no-]test3","-t") do - end - - @app.option_parser - end - - it "should pass a block that calls our defined method" do - OptionParser.any_instance.stubs(:on) - OptionParser.any_instance.stubs(:on).with('--test4','-t').yields(nil) - - @app.expects(:send).with(:handle_test4, nil) - - @app.class.option("--test4","-t") do - end - - @app.option_parser - end - end - - describe "when no block is given" do - it "should declare the option to OptionParser" do - OptionParser.any_instance.stubs(:on) - OptionParser.any_instance.expects(:on).with("--test4","-t") - - @app.class.option("--test4","-t") - - @app.option_parser - end - - it "should give to OptionParser a block that adds the the value to the options array" do - OptionParser.any_instance.stubs(:on) - OptionParser.any_instance.stubs(:on).with("--test4","-t").yields(nil) - - @app.options.expects(:[]=).with(:test4,nil) - - @app.class.option("--test4","-t") - - @app.option_parser - end - end - end - - end -end diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb new file mode 100755 index 000000000..80de8db48 --- /dev/null +++ b/spec/unit/configurer/downloader_spec.rb @@ -0,0 +1,188 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/configurer/downloader' + +describe Puppet::Configurer::Downloader do + it "should require a name" do + lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError) + end + + it "should require a path and a source at initialization" do + lambda { Puppet::Configurer::Downloader.new("name") }.should raise_error(ArgumentError) + end + + it "should set the name, path and source appropriately" do + dler = Puppet::Configurer::Downloader.new("facts", "path", "source") + dler.name.should == "facts" + dler.path.should == "path" + dler.source.should == "source" + end + + it "should be able to provide a timeout value" do + Puppet::Configurer::Downloader.should respond_to(:timeout) + end + + it "should use the configtimeout, converted to an integer, as its timeout" do + Puppet.settings.expects(:value).with(:configtimeout).returns "50" + Puppet::Configurer::Downloader.timeout.should == 50 + end + + describe "when creating the file that does the downloading" do + before do + @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") + end + + it "should create a file instance with the right path and source" do + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:path] == "path" and opts[:source] == "source" } + @dler.file + end + + it "should tag the file with the downloader name" do + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:tag] == "foo" } + @dler.file + end + + it "should always recurse" do + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:recurse] == true } + @dler.file + end + + it "should always purge" do + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:purge] == true } + @dler.file + end + + it "should never be in noop" do + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:noop] == false } + @dler.file + end + + it "should always set the owner to the current UID" do + Process.expects(:uid).returns 51 + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == 51 } + @dler.file + end + + it "should always set the group to the current GID" do + Process.expects(:gid).returns 61 + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == 61 } + @dler.file + end + + it "should always force the download" do + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:force] == true } + @dler.file + end + + it "should never back up when downloading" do + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:backup] == false } + @dler.file + end + + it "should support providing an 'ignore' parameter" do + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == [".svn"] } + @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn") + @dler.file + end + + it "should split the 'ignore' parameter on whitespace" do + Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == %w{.svn CVS} } + @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn CVS") + @dler.file + end + end + + describe "when creating the catalog to do the downloading" do + before do + @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") + end + + it "should create a catalog and add the file to it" do + file = mock 'file' + catalog = mock 'catalog' + + @dler.expects(:file).returns file + + Puppet::Resource::Catalog.expects(:new).returns catalog + catalog.expects(:add_resource).with(file) + + @dler.catalog.should equal(catalog) + end + end + + describe "when downloading" do + before do + @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") + end + + it "should log that it is downloading" do + Puppet.expects(:info) + Timeout.stubs(:timeout) + + @dler.evaluate + end + + it "should set a timeout for the download" do + Puppet::Configurer::Downloader.expects(:timeout).returns 50 + Timeout.expects(:timeout).with(50) + + @dler.evaluate + end + + it "should apply the catalog within the timeout block" do + catalog = mock 'catalog' + @dler.expects(:catalog).returns(catalog) + + Timeout.expects(:timeout).yields + + catalog.expects(:apply) + + @dler.evaluate + end + + it "should return all changed file paths" do + trans = mock 'transaction' + + catalog = mock 'catalog' + @dler.expects(:catalog).returns(catalog) + catalog.expects(:apply).yields(trans) + + Timeout.expects(:timeout).yields + + resource = mock 'resource' + resource.expects(:[]).with(:path).returns "/changed/file" + + trans.expects(:changed?).returns([resource]) + + @dler.evaluate.should == %w{/changed/file} + end + + it "should yield the resources if a block is given" do + trans = mock 'transaction' + + catalog = mock 'catalog' + @dler.expects(:catalog).returns(catalog) + catalog.expects(:apply).yields(trans) + + Timeout.expects(:timeout).yields + + resource = mock 'resource' + resource.expects(:[]).with(:path).returns "/changed/file" + + trans.expects(:changed?).returns([resource]) + + yielded = nil + @dler.evaluate { |r| yielded = r } + yielded.should == resource + end + + it "should catch and log exceptions" do + Puppet.expects(:err) + Timeout.stubs(:timeout).raises(Puppet::Error, "testing") + + lambda { @dler.evaluate }.should_not raise_error + end + end +end diff --git a/spec/unit/configurer/downloader_spec_spec.rb b/spec/unit/configurer/downloader_spec_spec.rb deleted file mode 100755 index 80de8db48..000000000 --- a/spec/unit/configurer/downloader_spec_spec.rb +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/configurer/downloader' - -describe Puppet::Configurer::Downloader do - it "should require a name" do - lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError) - end - - it "should require a path and a source at initialization" do - lambda { Puppet::Configurer::Downloader.new("name") }.should raise_error(ArgumentError) - end - - it "should set the name, path and source appropriately" do - dler = Puppet::Configurer::Downloader.new("facts", "path", "source") - dler.name.should == "facts" - dler.path.should == "path" - dler.source.should == "source" - end - - it "should be able to provide a timeout value" do - Puppet::Configurer::Downloader.should respond_to(:timeout) - end - - it "should use the configtimeout, converted to an integer, as its timeout" do - Puppet.settings.expects(:value).with(:configtimeout).returns "50" - Puppet::Configurer::Downloader.timeout.should == 50 - end - - describe "when creating the file that does the downloading" do - before do - @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") - end - - it "should create a file instance with the right path and source" do - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:path] == "path" and opts[:source] == "source" } - @dler.file - end - - it "should tag the file with the downloader name" do - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:tag] == "foo" } - @dler.file - end - - it "should always recurse" do - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:recurse] == true } - @dler.file - end - - it "should always purge" do - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:purge] == true } - @dler.file - end - - it "should never be in noop" do - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:noop] == false } - @dler.file - end - - it "should always set the owner to the current UID" do - Process.expects(:uid).returns 51 - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == 51 } - @dler.file - end - - it "should always set the group to the current GID" do - Process.expects(:gid).returns 61 - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == 61 } - @dler.file - end - - it "should always force the download" do - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:force] == true } - @dler.file - end - - it "should never back up when downloading" do - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:backup] == false } - @dler.file - end - - it "should support providing an 'ignore' parameter" do - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == [".svn"] } - @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn") - @dler.file - end - - it "should split the 'ignore' parameter on whitespace" do - Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == %w{.svn CVS} } - @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn CVS") - @dler.file - end - end - - describe "when creating the catalog to do the downloading" do - before do - @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") - end - - it "should create a catalog and add the file to it" do - file = mock 'file' - catalog = mock 'catalog' - - @dler.expects(:file).returns file - - Puppet::Resource::Catalog.expects(:new).returns catalog - catalog.expects(:add_resource).with(file) - - @dler.catalog.should equal(catalog) - end - end - - describe "when downloading" do - before do - @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") - end - - it "should log that it is downloading" do - Puppet.expects(:info) - Timeout.stubs(:timeout) - - @dler.evaluate - end - - it "should set a timeout for the download" do - Puppet::Configurer::Downloader.expects(:timeout).returns 50 - Timeout.expects(:timeout).with(50) - - @dler.evaluate - end - - it "should apply the catalog within the timeout block" do - catalog = mock 'catalog' - @dler.expects(:catalog).returns(catalog) - - Timeout.expects(:timeout).yields - - catalog.expects(:apply) - - @dler.evaluate - end - - it "should return all changed file paths" do - trans = mock 'transaction' - - catalog = mock 'catalog' - @dler.expects(:catalog).returns(catalog) - catalog.expects(:apply).yields(trans) - - Timeout.expects(:timeout).yields - - resource = mock 'resource' - resource.expects(:[]).with(:path).returns "/changed/file" - - trans.expects(:changed?).returns([resource]) - - @dler.evaluate.should == %w{/changed/file} - end - - it "should yield the resources if a block is given" do - trans = mock 'transaction' - - catalog = mock 'catalog' - @dler.expects(:catalog).returns(catalog) - catalog.expects(:apply).yields(trans) - - Timeout.expects(:timeout).yields - - resource = mock 'resource' - resource.expects(:[]).with(:path).returns "/changed/file" - - trans.expects(:changed?).returns([resource]) - - yielded = nil - @dler.evaluate { |r| yielded = r } - yielded.should == resource - end - - it "should catch and log exceptions" do - Puppet.expects(:err) - Timeout.stubs(:timeout).raises(Puppet::Error, "testing") - - lambda { @dler.evaluate }.should_not raise_error - end - end -end diff --git a/spec/unit/configurer/fact_handler_spec.rb b/spec/unit/configurer/fact_handler_spec.rb new file mode 100755 index 000000000..290f1acfc --- /dev/null +++ b/spec/unit/configurer/fact_handler_spec.rb @@ -0,0 +1,164 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/configurer' +require 'puppet/configurer/fact_handler' + +class FactHandlerTester + include Puppet::Configurer::FactHandler +end + +describe Puppet::Configurer::FactHandler do + before do + @facthandler = FactHandlerTester.new + end + + it "should have a method for downloading fact plugins" do + @facthandler.should respond_to(:download_fact_plugins) + end + + it "should have a boolean method for determining whether fact plugins should be downloaded" do + @facthandler.should respond_to(:download_fact_plugins?) + end + + it "should download fact plugins when :factsync is true" do + Puppet.settings.expects(:value).with(:factsync).returns true + @facthandler.should be_download_fact_plugins + end + + it "should not download fact plugins when :factsync is false" do + Puppet.settings.expects(:value).with(:factsync).returns false + @facthandler.should_not be_download_fact_plugins + end + + it "should not download fact plugins when downloading is disabled" do + Puppet::Configurer::Downloader.expects(:new).never + @facthandler.expects(:download_fact_plugins?).returns false + @facthandler.download_fact_plugins + end + + it "should use an Agent Downloader, with the name, source, destination, and ignore set correctly, to download fact plugins when downloading is enabled" do + downloader = mock 'downloader' + + Puppet.settings.expects(:value).with(:factsource).returns "fsource" + Puppet.settings.expects(:value).with(:factdest).returns "fdest" + Puppet.settings.expects(:value).with(:factsignore).returns "fignore" + + Puppet::Configurer::Downloader.expects(:new).with("fact", "fdest", "fsource", "fignore").returns downloader + + downloader.expects(:evaluate) + + @facthandler.expects(:download_fact_plugins?).returns true + @facthandler.download_fact_plugins + end + + it "should warn about factsync deprecation when factsync is enabled" do + Puppet::Configurer::Downloader.stubs(:new).returns mock("downloader", :evaluate => nil) + + @facthandler.expects(:download_fact_plugins?).returns true + Puppet.expects(:warning) + @facthandler.download_fact_plugins + end + + it "should have a method for retrieving facts" do + @facthandler.should respond_to(:find_facts) + end + + it "should use the Facts class with the :certname to find the facts" do + Puppet.settings.expects(:value).with(:certname).returns "foo" + Puppet::Node::Facts.expects(:find).with("foo").returns "myfacts" + @facthandler.stubs(:reload_facter) + @facthandler.find_facts.should == "myfacts" + end + + it "should reload Facter and find local facts when asked to find facts" do + @facthandler.expects(:reload_facter) + + Puppet.settings.expects(:value).with(:certname).returns "myhost" + Puppet::Node::Facts.expects(:find).with("myhost") + + @facthandler.find_facts + end + + it "should fail if finding facts fails" do + @facthandler.stubs(:reload_facter) + + Puppet.settings.stubs(:value).with(:trace).returns false + Puppet.settings.stubs(:value).with(:certname).returns "myhost" + Puppet::Node::Facts.expects(:find).raises RuntimeError + + lambda { @facthandler.find_facts }.should raise_error(Puppet::Error) + end + + it "should have a method to prepare the facts for uploading" do + @facthandler.should respond_to(:facts_for_uploading) + end + + # I couldn't get marshal to work for this, only yaml, so we hard-code yaml. + it "should serialize and CGI escape the fact values for uploading" do + facts = stub 'facts' + facts.expects(:support_format?).with(:b64_zlib_yaml).returns true + facts.expects(:render).returns "my text" + text = CGI.escape("my text") + + @facthandler.expects(:find_facts).returns facts + + @facthandler.facts_for_uploading.should == {:facts_format => :b64_zlib_yaml, :facts => text} + end + + it "should properly accept facts containing a '+'" do + facts = stub 'facts' + facts.expects(:support_format?).with(:b64_zlib_yaml).returns true + facts.expects(:render).returns "my+text" + text = "my%2Btext" + + @facthandler.expects(:find_facts).returns facts + + @facthandler.facts_for_uploading.should == {:facts_format => :b64_zlib_yaml, :facts => text} + end + + it "use compressed yaml as the serialization if zlib is supported" do + facts = stub 'facts' + facts.expects(:support_format?).with(:b64_zlib_yaml).returns true + facts.expects(:render).with(:b64_zlib_yaml).returns "my text" + text = CGI.escape("my text") + + @facthandler.expects(:find_facts).returns facts + + @facthandler.facts_for_uploading + end + + it "should use yaml as the serialization if zlib is not supported" do + facts = stub 'facts' + facts.expects(:support_format?).with(:b64_zlib_yaml).returns false + facts.expects(:render).with(:yaml).returns "my text" + text = CGI.escape("my text") + + @facthandler.expects(:find_facts).returns facts + + @facthandler.facts_for_uploading + end + + describe "when reloading Facter" do + before do + Facter.stubs(:clear) + Facter.stubs(:load) + Facter.stubs(:loadfacts) + end + + it "should clear Facter" do + Facter.expects(:clear) + @facthandler.reload_facter + end + + it "should load all Facter facts" do + Facter.expects(:loadfacts) + @facthandler.reload_facter + end + + it "should use the Facter terminus load all Puppet Fact plugins" do + Puppet::Node::Facts::Facter.expects(:load_fact_plugins) + @facthandler.reload_facter + end + end +end diff --git a/spec/unit/configurer/fact_handler_spec_spec.rb b/spec/unit/configurer/fact_handler_spec_spec.rb deleted file mode 100755 index 290f1acfc..000000000 --- a/spec/unit/configurer/fact_handler_spec_spec.rb +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/configurer' -require 'puppet/configurer/fact_handler' - -class FactHandlerTester - include Puppet::Configurer::FactHandler -end - -describe Puppet::Configurer::FactHandler do - before do - @facthandler = FactHandlerTester.new - end - - it "should have a method for downloading fact plugins" do - @facthandler.should respond_to(:download_fact_plugins) - end - - it "should have a boolean method for determining whether fact plugins should be downloaded" do - @facthandler.should respond_to(:download_fact_plugins?) - end - - it "should download fact plugins when :factsync is true" do - Puppet.settings.expects(:value).with(:factsync).returns true - @facthandler.should be_download_fact_plugins - end - - it "should not download fact plugins when :factsync is false" do - Puppet.settings.expects(:value).with(:factsync).returns false - @facthandler.should_not be_download_fact_plugins - end - - it "should not download fact plugins when downloading is disabled" do - Puppet::Configurer::Downloader.expects(:new).never - @facthandler.expects(:download_fact_plugins?).returns false - @facthandler.download_fact_plugins - end - - it "should use an Agent Downloader, with the name, source, destination, and ignore set correctly, to download fact plugins when downloading is enabled" do - downloader = mock 'downloader' - - Puppet.settings.expects(:value).with(:factsource).returns "fsource" - Puppet.settings.expects(:value).with(:factdest).returns "fdest" - Puppet.settings.expects(:value).with(:factsignore).returns "fignore" - - Puppet::Configurer::Downloader.expects(:new).with("fact", "fdest", "fsource", "fignore").returns downloader - - downloader.expects(:evaluate) - - @facthandler.expects(:download_fact_plugins?).returns true - @facthandler.download_fact_plugins - end - - it "should warn about factsync deprecation when factsync is enabled" do - Puppet::Configurer::Downloader.stubs(:new).returns mock("downloader", :evaluate => nil) - - @facthandler.expects(:download_fact_plugins?).returns true - Puppet.expects(:warning) - @facthandler.download_fact_plugins - end - - it "should have a method for retrieving facts" do - @facthandler.should respond_to(:find_facts) - end - - it "should use the Facts class with the :certname to find the facts" do - Puppet.settings.expects(:value).with(:certname).returns "foo" - Puppet::Node::Facts.expects(:find).with("foo").returns "myfacts" - @facthandler.stubs(:reload_facter) - @facthandler.find_facts.should == "myfacts" - end - - it "should reload Facter and find local facts when asked to find facts" do - @facthandler.expects(:reload_facter) - - Puppet.settings.expects(:value).with(:certname).returns "myhost" - Puppet::Node::Facts.expects(:find).with("myhost") - - @facthandler.find_facts - end - - it "should fail if finding facts fails" do - @facthandler.stubs(:reload_facter) - - Puppet.settings.stubs(:value).with(:trace).returns false - Puppet.settings.stubs(:value).with(:certname).returns "myhost" - Puppet::Node::Facts.expects(:find).raises RuntimeError - - lambda { @facthandler.find_facts }.should raise_error(Puppet::Error) - end - - it "should have a method to prepare the facts for uploading" do - @facthandler.should respond_to(:facts_for_uploading) - end - - # I couldn't get marshal to work for this, only yaml, so we hard-code yaml. - it "should serialize and CGI escape the fact values for uploading" do - facts = stub 'facts' - facts.expects(:support_format?).with(:b64_zlib_yaml).returns true - facts.expects(:render).returns "my text" - text = CGI.escape("my text") - - @facthandler.expects(:find_facts).returns facts - - @facthandler.facts_for_uploading.should == {:facts_format => :b64_zlib_yaml, :facts => text} - end - - it "should properly accept facts containing a '+'" do - facts = stub 'facts' - facts.expects(:support_format?).with(:b64_zlib_yaml).returns true - facts.expects(:render).returns "my+text" - text = "my%2Btext" - - @facthandler.expects(:find_facts).returns facts - - @facthandler.facts_for_uploading.should == {:facts_format => :b64_zlib_yaml, :facts => text} - end - - it "use compressed yaml as the serialization if zlib is supported" do - facts = stub 'facts' - facts.expects(:support_format?).with(:b64_zlib_yaml).returns true - facts.expects(:render).with(:b64_zlib_yaml).returns "my text" - text = CGI.escape("my text") - - @facthandler.expects(:find_facts).returns facts - - @facthandler.facts_for_uploading - end - - it "should use yaml as the serialization if zlib is not supported" do - facts = stub 'facts' - facts.expects(:support_format?).with(:b64_zlib_yaml).returns false - facts.expects(:render).with(:yaml).returns "my text" - text = CGI.escape("my text") - - @facthandler.expects(:find_facts).returns facts - - @facthandler.facts_for_uploading - end - - describe "when reloading Facter" do - before do - Facter.stubs(:clear) - Facter.stubs(:load) - Facter.stubs(:loadfacts) - end - - it "should clear Facter" do - Facter.expects(:clear) - @facthandler.reload_facter - end - - it "should load all Facter facts" do - Facter.expects(:loadfacts) - @facthandler.reload_facter - end - - it "should use the Facter terminus load all Puppet Fact plugins" do - Puppet::Node::Facts::Facter.expects(:load_fact_plugins) - @facthandler.reload_facter - end - end -end diff --git a/spec/unit/configurer/plugin_handler_spec.rb b/spec/unit/configurer/plugin_handler_spec.rb new file mode 100755 index 000000000..7f59d5bb3 --- /dev/null +++ b/spec/unit/configurer/plugin_handler_spec.rb @@ -0,0 +1,112 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/configurer' +require 'puppet/configurer/plugin_handler' + +class PluginHandlerTester + include Puppet::Configurer::PluginHandler +end + +describe Puppet::Configurer::PluginHandler do + before do + @pluginhandler = PluginHandlerTester.new + end + + it "should have a method for downloading plugins" do + @pluginhandler.should respond_to(:download_plugins) + end + + it "should have a boolean method for determining whether plugins should be downloaded" do + @pluginhandler.should respond_to(:download_plugins?) + end + + it "should download plugins when :pluginsync is true" do + Puppet.settings.expects(:value).with(:pluginsync).returns true + @pluginhandler.should be_download_plugins + end + + it "should not download plugins when :pluginsync is false" do + Puppet.settings.expects(:value).with(:pluginsync).returns false + @pluginhandler.should_not be_download_plugins + end + + it "should not download plugins when downloading is disabled" do + Puppet::Configurer::Downloader.expects(:new).never + @pluginhandler.expects(:download_plugins?).returns false + @pluginhandler.download_plugins + end + + it "should use an Agent Downloader, with the name, source, destination, and ignore set correctly, to download plugins when downloading is enabled" do + downloader = mock 'downloader' + + Puppet.settings.expects(:value).with(:pluginsource).returns "psource" + Puppet.settings.expects(:value).with(:plugindest).returns "pdest" + Puppet.settings.expects(:value).with(:pluginsignore).returns "pignore" + + Puppet::Configurer::Downloader.expects(:new).with("plugin", "pdest", "psource", "pignore").returns downloader + + downloader.expects(:evaluate).returns [] + + @pluginhandler.expects(:download_plugins?).returns true + @pluginhandler.download_plugins + end + + it "should be able to load plugins" do + @pluginhandler.should respond_to(:load_plugin) + end + + it "should load each downloaded file" do + FileTest.stubs(:exist?).returns true + downloader = mock 'downloader' + + Puppet::Configurer::Downloader.expects(:new).returns downloader + + downloader.expects(:evaluate).returns %w{one two} + + @pluginhandler.expects(:download_plugins?).returns true + + @pluginhandler.expects(:load_plugin).with("one") + @pluginhandler.expects(:load_plugin).with("two") + + @pluginhandler.download_plugins + end + + it "should load plugins when asked to do so" do + FileTest.stubs(:exist?).returns true + @pluginhandler.expects(:load).with("foo") + + @pluginhandler.load_plugin("foo") + end + + it "should not try to load files that don't exist" do + FileTest.expects(:exist?).with("foo").returns true + @pluginhandler.expects(:load).never + + @pluginhandler.load_plugin("foo") + end + + it "should not try to load directories" do + FileTest.stubs(:exist?).returns true + FileTest.expects(:directory?).with("foo").returns true + @pluginhandler.expects(:load).never + + @pluginhandler.load_plugin("foo") + end + + it "should warn but not fail if loading a file raises an exception" do + FileTest.stubs(:exist?).returns true + @pluginhandler.expects(:load).with("foo").raises "eh" + + Puppet.expects(:err) + @pluginhandler.load_plugin("foo") + end + + it "should warn but not fail if loading a file raises a LoadError" do + FileTest.stubs(:exist?).returns true + @pluginhandler.expects(:load).with("foo").raises LoadError.new("eh") + + Puppet.expects(:err) + @pluginhandler.load_plugin("foo") + end +end diff --git a/spec/unit/configurer/plugin_handler_spec_spec.rb b/spec/unit/configurer/plugin_handler_spec_spec.rb deleted file mode 100755 index 7f59d5bb3..000000000 --- a/spec/unit/configurer/plugin_handler_spec_spec.rb +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/configurer' -require 'puppet/configurer/plugin_handler' - -class PluginHandlerTester - include Puppet::Configurer::PluginHandler -end - -describe Puppet::Configurer::PluginHandler do - before do - @pluginhandler = PluginHandlerTester.new - end - - it "should have a method for downloading plugins" do - @pluginhandler.should respond_to(:download_plugins) - end - - it "should have a boolean method for determining whether plugins should be downloaded" do - @pluginhandler.should respond_to(:download_plugins?) - end - - it "should download plugins when :pluginsync is true" do - Puppet.settings.expects(:value).with(:pluginsync).returns true - @pluginhandler.should be_download_plugins - end - - it "should not download plugins when :pluginsync is false" do - Puppet.settings.expects(:value).with(:pluginsync).returns false - @pluginhandler.should_not be_download_plugins - end - - it "should not download plugins when downloading is disabled" do - Puppet::Configurer::Downloader.expects(:new).never - @pluginhandler.expects(:download_plugins?).returns false - @pluginhandler.download_plugins - end - - it "should use an Agent Downloader, with the name, source, destination, and ignore set correctly, to download plugins when downloading is enabled" do - downloader = mock 'downloader' - - Puppet.settings.expects(:value).with(:pluginsource).returns "psource" - Puppet.settings.expects(:value).with(:plugindest).returns "pdest" - Puppet.settings.expects(:value).with(:pluginsignore).returns "pignore" - - Puppet::Configurer::Downloader.expects(:new).with("plugin", "pdest", "psource", "pignore").returns downloader - - downloader.expects(:evaluate).returns [] - - @pluginhandler.expects(:download_plugins?).returns true - @pluginhandler.download_plugins - end - - it "should be able to load plugins" do - @pluginhandler.should respond_to(:load_plugin) - end - - it "should load each downloaded file" do - FileTest.stubs(:exist?).returns true - downloader = mock 'downloader' - - Puppet::Configurer::Downloader.expects(:new).returns downloader - - downloader.expects(:evaluate).returns %w{one two} - - @pluginhandler.expects(:download_plugins?).returns true - - @pluginhandler.expects(:load_plugin).with("one") - @pluginhandler.expects(:load_plugin).with("two") - - @pluginhandler.download_plugins - end - - it "should load plugins when asked to do so" do - FileTest.stubs(:exist?).returns true - @pluginhandler.expects(:load).with("foo") - - @pluginhandler.load_plugin("foo") - end - - it "should not try to load files that don't exist" do - FileTest.expects(:exist?).with("foo").returns true - @pluginhandler.expects(:load).never - - @pluginhandler.load_plugin("foo") - end - - it "should not try to load directories" do - FileTest.stubs(:exist?).returns true - FileTest.expects(:directory?).with("foo").returns true - @pluginhandler.expects(:load).never - - @pluginhandler.load_plugin("foo") - end - - it "should warn but not fail if loading a file raises an exception" do - FileTest.stubs(:exist?).returns true - @pluginhandler.expects(:load).with("foo").raises "eh" - - Puppet.expects(:err) - @pluginhandler.load_plugin("foo") - end - - it "should warn but not fail if loading a file raises a LoadError" do - FileTest.stubs(:exist?).returns true - @pluginhandler.expects(:load).with("foo").raises LoadError.new("eh") - - Puppet.expects(:err) - @pluginhandler.load_plugin("foo") - end -end diff --git a/spec/unit/configurer_spec.rb b/spec/unit/configurer_spec.rb new file mode 100755 index 000000000..377ac74b4 --- /dev/null +++ b/spec/unit/configurer_spec.rb @@ -0,0 +1,494 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-11-12. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../spec_helper' +require 'puppet/configurer' + +describe Puppet::Configurer do + before do + Puppet.settings.stubs(:use).returns(true) + @agent = Puppet::Configurer.new + end + + it "should include the Plugin Handler module" do + Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::PluginHandler) + end + + it "should include the Fact Handler module" do + Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::FactHandler) + end + + it "should use the puppetdlockfile as its lockfile path" do + Puppet.settings.expects(:value).with(:puppetdlockfile).returns("/my/lock") + Puppet::Configurer.lockfile_path.should == "/my/lock" + end + + describe "when executing a pre-run hook" do + it "should do nothing if the hook is set to an empty string" do + Puppet.settings[:prerun_command] = "" + Puppet::Util.expects(:exec).never + + @agent.execute_prerun_command + end + + it "should execute any pre-run command provided via the 'prerun_command' setting" do + Puppet.settings[:prerun_command] = "/my/command" + Puppet::Util.expects(:execute).with { |args| args[0] == "/my/command" } + + @agent.execute_prerun_command + end + + it "should fail if the command fails" do + Puppet.settings[:prerun_command] = "/my/command" + Puppet::Util.expects(:execute).raises Puppet::ExecutionFailure + + lambda { @agent.execute_prerun_command }.should raise_error(Puppet::Configurer::CommandHookError) + end + end + + describe "when executing a post-run hook" do + it "should do nothing if the hook is set to an empty string" do + Puppet.settings[:postrun_command] = "" + Puppet::Util.expects(:exec).never + + @agent.execute_postrun_command + end + + it "should execute any post-run command provided via the 'postrun_command' setting" do + Puppet.settings[:postrun_command] = "/my/command" + Puppet::Util.expects(:execute).with { |args| args[0] == "/my/command" } + + @agent.execute_postrun_command + end + + it "should fail if the command fails" do + Puppet.settings[:postrun_command] = "/my/command" + Puppet::Util.expects(:execute).raises Puppet::ExecutionFailure + + lambda { @agent.execute_postrun_command }.should raise_error(Puppet::Configurer::CommandHookError) + end + end +end + +describe Puppet::Configurer, "when initializing a report" do + it "should return an instance of a transaction report" do + Puppet.settings.stubs(:use).returns(true) + @agent = Puppet::Configurer.new + @agent.initialize_report.should be_instance_of(Puppet::Transaction::Report) + end +end + +describe Puppet::Configurer, "when executing a catalog run" do + before do + Puppet.settings.stubs(:use).returns(true) + @agent = Puppet::Configurer.new + @agent.stubs(:prepare) + @agent.stubs(:facts_for_uploading).returns({}) + @catalog = Puppet::Resource::Catalog.new + @catalog.stubs(:apply) + @agent.stubs(:retrieve_catalog).returns @catalog + + Puppet::Util::Log.stubs(:newdestination) + Puppet::Util::Log.stubs(:close) + end + + it "should prepare for the run" do + @agent.expects(:prepare) + + @agent.run + end + + it "should initialize a transaction report if one is not provided" do + report = stub 'report' + @agent.expects(:initialize_report).returns report + + @agent.run + end + + it "should pass the new report to the catalog" do + report = stub 'report' + @agent.stubs(:initialize_report).returns report + @catalog.expects(:apply).with{|options| options[:report] == report} + + @agent.run + end + + it "should use the provided report if it was passed one" do + report = stub 'report' + @agent.expects(:initialize_report).never + @catalog.expects(:apply).with{|options| options[:report] == report} + + @agent.run(:report => report) + end + + it "should set the report as a log destination" do + report = stub 'report' + @agent.expects(:initialize_report).returns report + + Puppet::Util::Log.expects(:newdestination).with(report) + + @agent.run + end + + it "should retrieve the catalog" do + @agent.expects(:retrieve_catalog) + + @agent.run + end + + it "should log a failure and do nothing if no catalog can be retrieved" do + @agent.expects(:retrieve_catalog).returns nil + + Puppet.expects(:err).with "Could not retrieve catalog; skipping run" + + @agent.run + end + + it "should apply the catalog with all options to :run" do + @agent.expects(:retrieve_catalog).returns @catalog + + @catalog.expects(:apply).with { |args| args[:one] == true } + @agent.run :one => true + end + + it "should accept a catalog and use it instead of retrieving a different one" do + @agent.expects(:retrieve_catalog).never + + @catalog.expects(:apply) + @agent.run :one => true, :catalog => @catalog + end + + it "should benchmark how long it takes to apply the catalog" do + @agent.expects(:benchmark).with(:notice, "Finished catalog run") + + @agent.expects(:retrieve_catalog).returns @catalog + + @catalog.expects(:apply).never # because we're not yielding + @agent.run + end + + it "should execute post-run hooks after the run" do + @agent.expects(:execute_postrun_command) + + @agent.run + end + + it "should send the report" do + report = stub 'report' + @agent.expects(:initialize_report).returns report + @agent.expects(:send_report).with { |r, trans| r == report } + + @agent.run + end + + it "should send the transaction report with a reference to the transaction if a run was actually made" do + report = stub 'report' + @agent.expects(:initialize_report).returns report + + trans = stub 'transaction' + @catalog.expects(:apply).returns trans + + @agent.expects(:send_report).with { |r, t| t == trans } + + @agent.run :catalog => @catalog + end + + it "should send the transaction report even if the catalog could not be retrieved" do + @agent.expects(:retrieve_catalog).returns nil + + report = stub 'report' + @agent.expects(:initialize_report).returns report + @agent.expects(:send_report) + + @agent.run + end + + it "should send the transaction report even if there is a failure" do + @agent.expects(:retrieve_catalog).raises "whatever" + + report = stub 'report' + @agent.expects(:initialize_report).returns report + @agent.expects(:send_report) + + lambda { @agent.run }.should raise_error + end + + it "should remove the report as a log destination when the run is finished" do + report = stub 'report' + @agent.expects(:initialize_report).returns report + + Puppet::Util::Log.expects(:close).with(report) + + @agent.run + end + + it "should return the report as the result of the run" do + report = stub 'report' + @agent.expects(:initialize_report).returns report + + @agent.run.should equal(report) + end +end + +describe Puppet::Configurer, "when sending a report" do + before do + Puppet.settings.stubs(:use).returns(true) + @configurer = Puppet::Configurer.new + + @report = stub 'report' + @trans = stub 'transaction' + end + + it "should require a report" do + lambda { @configurer.send_report }.should raise_error(ArgumentError) + end + + it "should allow specification of a transaction" do + lambda { @configurer.send_report(@report, @trans) }.should_not raise_error(ArgumentError) + end + + it "should use any provided transaction to add metrics to the report" do + @trans.expects(:generate_report) + @configurer.send_report(@report, @trans) + end + + it "should print a report summary if configured to do so" do + Puppet.settings[:summarize] = true + + @report.expects(:summary).returns "stuff" + + @configurer.expects(:puts).with("stuff") + @configurer.send_report(@report) + end + + it "should not print a report summary if not configured to do so" do + Puppet.settings[:summarize] = false + + @configurer.expects(:puts).never + @configurer.send_report(@report) + end + + it "should save the report if reporting is enabled" do + Puppet.settings[:report] = true + + @report.expects(:save) + @configurer.send_report(@report) + end + + it "should not save the report if reporting is disabled" do + Puppet.settings[:report] = false + + @report.expects(:save).never + @configurer.send_report(@report) + end + + it "should log but not fail if saving the report fails" do + Puppet.settings[:report] = true + + @report.expects(:save).raises "whatever" + + Puppet.expects(:err) + lambda { @configurer.send_report(@report) }.should_not raise_error + end +end + +describe Puppet::Configurer, "when retrieving a catalog" do + before do + Puppet.settings.stubs(:use).returns(true) + @agent = Puppet::Configurer.new + @agent.stubs(:facts_for_uploading).returns({}) + + @catalog = Puppet::Resource::Catalog.new + + # this is the default when using a Configurer instance + Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :rest + + @agent.stubs(:convert_catalog).returns @catalog + end + + describe "and configured to only retrieve a catalog from the cache" do + before do + Puppet.settings[:use_cached_catalog] = true + end + + it "should first look in the cache for a catalog" do + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.never + + @agent.retrieve_catalog.should == @catalog + end + + it "should compile a new catalog if none is found in the cache" do + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog + + @agent.retrieve_catalog.should == @catalog + end + end + + describe "when not using a REST terminus for catalogs" do + it "should not pass any facts when retrieving the catalog" do + @agent.expects(:facts_for_uploading).never + Puppet::Resource::Catalog.expects(:find).with { |name, options| + options[:facts].nil? + }.returns @catalog + + @agent.retrieve_catalog + end + end + + describe "when using a REST terminus for catalogs" do + it "should pass the prepared facts and the facts format as arguments when retrieving the catalog" do + @agent.expects(:facts_for_uploading).returns(:facts => "myfacts", :facts_format => :foo) + Puppet::Resource::Catalog.expects(:find).with { |name, options| + options[:facts] == "myfacts" and options[:facts_format] == :foo + }.returns @catalog + + @agent.retrieve_catalog + end + end + + it "should use the Catalog class to get its catalog" do + Puppet::Resource::Catalog.expects(:find).returns @catalog + + @agent.retrieve_catalog + end + + it "should use its certname to retrieve the catalog" do + Facter.stubs(:value).returns "eh" + Puppet.settings[:certname] = "myhost.domain.com" + Puppet::Resource::Catalog.expects(:find).with { |name, options| name == "myhost.domain.com" }.returns @catalog + + @agent.retrieve_catalog + end + + it "should default to returning a catalog retrieved directly from the server, skipping the cache" do + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog + + @agent.retrieve_catalog.should == @catalog + end + + it "should log and return the cached catalog when no catalog can be retrieved from the server" do + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog + + Puppet.expects(:notice) + + @agent.retrieve_catalog.should == @catalog + end + + it "should not look in the cache for a catalog if one is returned from the server" do + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.never + + @agent.retrieve_catalog.should == @catalog + end + + it "should return the cached catalog when retrieving the remote catalog throws an exception" do + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.raises "eh" + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog + + @agent.retrieve_catalog.should == @catalog + end + + it "should log and return nil if no catalog can be retrieved from the server and :usecacheonfailure is disabled" do + Puppet.stubs(:[]) + Puppet.expects(:[]).with(:usecacheonfailure).returns false + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil + + Puppet.expects(:warning) + + @agent.retrieve_catalog.should be_nil + end + + it "should return nil if no cached catalog is available and no catalog can be retrieved from the server" do + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil + Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil + + @agent.retrieve_catalog.should be_nil + end + + it "should convert the catalog before returning" do + Puppet::Resource::Catalog.stubs(:find).returns @catalog + + @agent.expects(:convert_catalog).with { |cat, dur| cat == @catalog }.returns "converted catalog" + @agent.retrieve_catalog.should == "converted catalog" + end + + it "should return nil if there is an error while retrieving the catalog" do + Puppet::Resource::Catalog.expects(:find).raises "eh" + + @agent.retrieve_catalog.should be_nil + end +end + +describe Puppet::Configurer, "when converting the catalog" do + before do + Puppet.settings.stubs(:use).returns(true) + @agent = Puppet::Configurer.new + + @catalog = Puppet::Resource::Catalog.new + @oldcatalog = stub 'old_catalog', :to_ral => @catalog + end + + it "should convert the catalog to a RAL-formed catalog" do + @oldcatalog.expects(:to_ral).returns @catalog + + @agent.convert_catalog(@oldcatalog, 10).should equal(@catalog) + end + + it "should finalize the catalog" do + @catalog.expects(:finalize) + + @agent.convert_catalog(@oldcatalog, 10) + end + + it "should record the passed retrieval time with the RAL catalog" do + @catalog.expects(:retrieval_duration=).with 10 + + @agent.convert_catalog(@oldcatalog, 10) + end + + it "should write the RAL catalog's class file" do + @catalog.expects(:write_class_file) + + @agent.convert_catalog(@oldcatalog, 10) + end +end + +describe Puppet::Configurer, "when preparing for a run" do + before do + Puppet.settings.stubs(:use).returns(true) + @agent = Puppet::Configurer.new + @agent.stubs(:dostorage) + @agent.stubs(:download_fact_plugins) + @agent.stubs(:download_plugins) + @agent.stubs(:execute_prerun_command) + @facts = {"one" => "two", "three" => "four"} + end + + it "should initialize the metadata store" do + @agent.class.stubs(:facts).returns(@facts) + @agent.expects(:dostorage) + @agent.prepare + end + + it "should download fact plugins" do + @agent.expects(:download_fact_plugins) + + @agent.prepare + end + + it "should download plugins" do + @agent.expects(:download_plugins) + + @agent.prepare + end + + it "should perform the pre-run commands" do + @agent.expects(:execute_prerun_command) + @agent.prepare + end +end diff --git a/spec/unit/configurer_spec_spec.rb b/spec/unit/configurer_spec_spec.rb deleted file mode 100755 index 377ac74b4..000000000 --- a/spec/unit/configurer_spec_spec.rb +++ /dev/null @@ -1,494 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-11-12. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../spec_helper' -require 'puppet/configurer' - -describe Puppet::Configurer do - before do - Puppet.settings.stubs(:use).returns(true) - @agent = Puppet::Configurer.new - end - - it "should include the Plugin Handler module" do - Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::PluginHandler) - end - - it "should include the Fact Handler module" do - Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::FactHandler) - end - - it "should use the puppetdlockfile as its lockfile path" do - Puppet.settings.expects(:value).with(:puppetdlockfile).returns("/my/lock") - Puppet::Configurer.lockfile_path.should == "/my/lock" - end - - describe "when executing a pre-run hook" do - it "should do nothing if the hook is set to an empty string" do - Puppet.settings[:prerun_command] = "" - Puppet::Util.expects(:exec).never - - @agent.execute_prerun_command - end - - it "should execute any pre-run command provided via the 'prerun_command' setting" do - Puppet.settings[:prerun_command] = "/my/command" - Puppet::Util.expects(:execute).with { |args| args[0] == "/my/command" } - - @agent.execute_prerun_command - end - - it "should fail if the command fails" do - Puppet.settings[:prerun_command] = "/my/command" - Puppet::Util.expects(:execute).raises Puppet::ExecutionFailure - - lambda { @agent.execute_prerun_command }.should raise_error(Puppet::Configurer::CommandHookError) - end - end - - describe "when executing a post-run hook" do - it "should do nothing if the hook is set to an empty string" do - Puppet.settings[:postrun_command] = "" - Puppet::Util.expects(:exec).never - - @agent.execute_postrun_command - end - - it "should execute any post-run command provided via the 'postrun_command' setting" do - Puppet.settings[:postrun_command] = "/my/command" - Puppet::Util.expects(:execute).with { |args| args[0] == "/my/command" } - - @agent.execute_postrun_command - end - - it "should fail if the command fails" do - Puppet.settings[:postrun_command] = "/my/command" - Puppet::Util.expects(:execute).raises Puppet::ExecutionFailure - - lambda { @agent.execute_postrun_command }.should raise_error(Puppet::Configurer::CommandHookError) - end - end -end - -describe Puppet::Configurer, "when initializing a report" do - it "should return an instance of a transaction report" do - Puppet.settings.stubs(:use).returns(true) - @agent = Puppet::Configurer.new - @agent.initialize_report.should be_instance_of(Puppet::Transaction::Report) - end -end - -describe Puppet::Configurer, "when executing a catalog run" do - before do - Puppet.settings.stubs(:use).returns(true) - @agent = Puppet::Configurer.new - @agent.stubs(:prepare) - @agent.stubs(:facts_for_uploading).returns({}) - @catalog = Puppet::Resource::Catalog.new - @catalog.stubs(:apply) - @agent.stubs(:retrieve_catalog).returns @catalog - - Puppet::Util::Log.stubs(:newdestination) - Puppet::Util::Log.stubs(:close) - end - - it "should prepare for the run" do - @agent.expects(:prepare) - - @agent.run - end - - it "should initialize a transaction report if one is not provided" do - report = stub 'report' - @agent.expects(:initialize_report).returns report - - @agent.run - end - - it "should pass the new report to the catalog" do - report = stub 'report' - @agent.stubs(:initialize_report).returns report - @catalog.expects(:apply).with{|options| options[:report] == report} - - @agent.run - end - - it "should use the provided report if it was passed one" do - report = stub 'report' - @agent.expects(:initialize_report).never - @catalog.expects(:apply).with{|options| options[:report] == report} - - @agent.run(:report => report) - end - - it "should set the report as a log destination" do - report = stub 'report' - @agent.expects(:initialize_report).returns report - - Puppet::Util::Log.expects(:newdestination).with(report) - - @agent.run - end - - it "should retrieve the catalog" do - @agent.expects(:retrieve_catalog) - - @agent.run - end - - it "should log a failure and do nothing if no catalog can be retrieved" do - @agent.expects(:retrieve_catalog).returns nil - - Puppet.expects(:err).with "Could not retrieve catalog; skipping run" - - @agent.run - end - - it "should apply the catalog with all options to :run" do - @agent.expects(:retrieve_catalog).returns @catalog - - @catalog.expects(:apply).with { |args| args[:one] == true } - @agent.run :one => true - end - - it "should accept a catalog and use it instead of retrieving a different one" do - @agent.expects(:retrieve_catalog).never - - @catalog.expects(:apply) - @agent.run :one => true, :catalog => @catalog - end - - it "should benchmark how long it takes to apply the catalog" do - @agent.expects(:benchmark).with(:notice, "Finished catalog run") - - @agent.expects(:retrieve_catalog).returns @catalog - - @catalog.expects(:apply).never # because we're not yielding - @agent.run - end - - it "should execute post-run hooks after the run" do - @agent.expects(:execute_postrun_command) - - @agent.run - end - - it "should send the report" do - report = stub 'report' - @agent.expects(:initialize_report).returns report - @agent.expects(:send_report).with { |r, trans| r == report } - - @agent.run - end - - it "should send the transaction report with a reference to the transaction if a run was actually made" do - report = stub 'report' - @agent.expects(:initialize_report).returns report - - trans = stub 'transaction' - @catalog.expects(:apply).returns trans - - @agent.expects(:send_report).with { |r, t| t == trans } - - @agent.run :catalog => @catalog - end - - it "should send the transaction report even if the catalog could not be retrieved" do - @agent.expects(:retrieve_catalog).returns nil - - report = stub 'report' - @agent.expects(:initialize_report).returns report - @agent.expects(:send_report) - - @agent.run - end - - it "should send the transaction report even if there is a failure" do - @agent.expects(:retrieve_catalog).raises "whatever" - - report = stub 'report' - @agent.expects(:initialize_report).returns report - @agent.expects(:send_report) - - lambda { @agent.run }.should raise_error - end - - it "should remove the report as a log destination when the run is finished" do - report = stub 'report' - @agent.expects(:initialize_report).returns report - - Puppet::Util::Log.expects(:close).with(report) - - @agent.run - end - - it "should return the report as the result of the run" do - report = stub 'report' - @agent.expects(:initialize_report).returns report - - @agent.run.should equal(report) - end -end - -describe Puppet::Configurer, "when sending a report" do - before do - Puppet.settings.stubs(:use).returns(true) - @configurer = Puppet::Configurer.new - - @report = stub 'report' - @trans = stub 'transaction' - end - - it "should require a report" do - lambda { @configurer.send_report }.should raise_error(ArgumentError) - end - - it "should allow specification of a transaction" do - lambda { @configurer.send_report(@report, @trans) }.should_not raise_error(ArgumentError) - end - - it "should use any provided transaction to add metrics to the report" do - @trans.expects(:generate_report) - @configurer.send_report(@report, @trans) - end - - it "should print a report summary if configured to do so" do - Puppet.settings[:summarize] = true - - @report.expects(:summary).returns "stuff" - - @configurer.expects(:puts).with("stuff") - @configurer.send_report(@report) - end - - it "should not print a report summary if not configured to do so" do - Puppet.settings[:summarize] = false - - @configurer.expects(:puts).never - @configurer.send_report(@report) - end - - it "should save the report if reporting is enabled" do - Puppet.settings[:report] = true - - @report.expects(:save) - @configurer.send_report(@report) - end - - it "should not save the report if reporting is disabled" do - Puppet.settings[:report] = false - - @report.expects(:save).never - @configurer.send_report(@report) - end - - it "should log but not fail if saving the report fails" do - Puppet.settings[:report] = true - - @report.expects(:save).raises "whatever" - - Puppet.expects(:err) - lambda { @configurer.send_report(@report) }.should_not raise_error - end -end - -describe Puppet::Configurer, "when retrieving a catalog" do - before do - Puppet.settings.stubs(:use).returns(true) - @agent = Puppet::Configurer.new - @agent.stubs(:facts_for_uploading).returns({}) - - @catalog = Puppet::Resource::Catalog.new - - # this is the default when using a Configurer instance - Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :rest - - @agent.stubs(:convert_catalog).returns @catalog - end - - describe "and configured to only retrieve a catalog from the cache" do - before do - Puppet.settings[:use_cached_catalog] = true - end - - it "should first look in the cache for a catalog" do - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.never - - @agent.retrieve_catalog.should == @catalog - end - - it "should compile a new catalog if none is found in the cache" do - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog - - @agent.retrieve_catalog.should == @catalog - end - end - - describe "when not using a REST terminus for catalogs" do - it "should not pass any facts when retrieving the catalog" do - @agent.expects(:facts_for_uploading).never - Puppet::Resource::Catalog.expects(:find).with { |name, options| - options[:facts].nil? - }.returns @catalog - - @agent.retrieve_catalog - end - end - - describe "when using a REST terminus for catalogs" do - it "should pass the prepared facts and the facts format as arguments when retrieving the catalog" do - @agent.expects(:facts_for_uploading).returns(:facts => "myfacts", :facts_format => :foo) - Puppet::Resource::Catalog.expects(:find).with { |name, options| - options[:facts] == "myfacts" and options[:facts_format] == :foo - }.returns @catalog - - @agent.retrieve_catalog - end - end - - it "should use the Catalog class to get its catalog" do - Puppet::Resource::Catalog.expects(:find).returns @catalog - - @agent.retrieve_catalog - end - - it "should use its certname to retrieve the catalog" do - Facter.stubs(:value).returns "eh" - Puppet.settings[:certname] = "myhost.domain.com" - Puppet::Resource::Catalog.expects(:find).with { |name, options| name == "myhost.domain.com" }.returns @catalog - - @agent.retrieve_catalog - end - - it "should default to returning a catalog retrieved directly from the server, skipping the cache" do - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog - - @agent.retrieve_catalog.should == @catalog - end - - it "should log and return the cached catalog when no catalog can be retrieved from the server" do - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog - - Puppet.expects(:notice) - - @agent.retrieve_catalog.should == @catalog - end - - it "should not look in the cache for a catalog if one is returned from the server" do - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.never - - @agent.retrieve_catalog.should == @catalog - end - - it "should return the cached catalog when retrieving the remote catalog throws an exception" do - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.raises "eh" - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog - - @agent.retrieve_catalog.should == @catalog - end - - it "should log and return nil if no catalog can be retrieved from the server and :usecacheonfailure is disabled" do - Puppet.stubs(:[]) - Puppet.expects(:[]).with(:usecacheonfailure).returns false - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil - - Puppet.expects(:warning) - - @agent.retrieve_catalog.should be_nil - end - - it "should return nil if no cached catalog is available and no catalog can be retrieved from the server" do - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil - Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil - - @agent.retrieve_catalog.should be_nil - end - - it "should convert the catalog before returning" do - Puppet::Resource::Catalog.stubs(:find).returns @catalog - - @agent.expects(:convert_catalog).with { |cat, dur| cat == @catalog }.returns "converted catalog" - @agent.retrieve_catalog.should == "converted catalog" - end - - it "should return nil if there is an error while retrieving the catalog" do - Puppet::Resource::Catalog.expects(:find).raises "eh" - - @agent.retrieve_catalog.should be_nil - end -end - -describe Puppet::Configurer, "when converting the catalog" do - before do - Puppet.settings.stubs(:use).returns(true) - @agent = Puppet::Configurer.new - - @catalog = Puppet::Resource::Catalog.new - @oldcatalog = stub 'old_catalog', :to_ral => @catalog - end - - it "should convert the catalog to a RAL-formed catalog" do - @oldcatalog.expects(:to_ral).returns @catalog - - @agent.convert_catalog(@oldcatalog, 10).should equal(@catalog) - end - - it "should finalize the catalog" do - @catalog.expects(:finalize) - - @agent.convert_catalog(@oldcatalog, 10) - end - - it "should record the passed retrieval time with the RAL catalog" do - @catalog.expects(:retrieval_duration=).with 10 - - @agent.convert_catalog(@oldcatalog, 10) - end - - it "should write the RAL catalog's class file" do - @catalog.expects(:write_class_file) - - @agent.convert_catalog(@oldcatalog, 10) - end -end - -describe Puppet::Configurer, "when preparing for a run" do - before do - Puppet.settings.stubs(:use).returns(true) - @agent = Puppet::Configurer.new - @agent.stubs(:dostorage) - @agent.stubs(:download_fact_plugins) - @agent.stubs(:download_plugins) - @agent.stubs(:execute_prerun_command) - @facts = {"one" => "two", "three" => "four"} - end - - it "should initialize the metadata store" do - @agent.class.stubs(:facts).returns(@facts) - @agent.expects(:dostorage) - @agent.prepare - end - - it "should download fact plugins" do - @agent.expects(:download_fact_plugins) - - @agent.prepare - end - - it "should download plugins" do - @agent.expects(:download_plugins) - - @agent.prepare - end - - it "should perform the pre-run commands" do - @agent.expects(:execute_prerun_command) - @agent.prepare - end -end diff --git a/spec/unit/daemon_spec.rb b/spec/unit/daemon_spec.rb new file mode 100755 index 000000000..e163f4067 --- /dev/null +++ b/spec/unit/daemon_spec.rb @@ -0,0 +1,306 @@ +#!/usr/bin/env ruby" + +require File.dirname(__FILE__) + '/../spec_helper' +require 'puppet/daemon' + +def without_warnings + flag = $VERBOSE + $VERBOSE = nil + yield + $VERBOSE = flag +end + +describe Puppet::Daemon do + before do + @daemon = Puppet::Daemon.new + end + + it "should be able to manage an agent" do + @daemon.should respond_to(:agent) + end + + it "should be able to manage a network server" do + @daemon.should respond_to(:server) + end + + it "should reopen the Log logs when told to reopen logs" do + Puppet::Util::Log.expects(:reopen) + @daemon.reopen_logs + end + + describe "when setting signal traps" do + before do + @daemon.stubs(:trap) + end + + {:INT => :stop, :TERM => :stop, :HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}.each do |signal, method| + it "should log and call #{method} when it receives #{signal}" do + @daemon.expects(:trap).with(signal).yields + + Puppet.expects(:notice) + + @daemon.expects(method) + + @daemon.set_signal_traps + end + end + end + + describe "when starting" do + before do + @daemon.stubs(:create_pidfile) + @daemon.stubs(:set_signal_traps) + EventLoop.current.stubs(:run) + end + + it "should fail if it has neither agent nor server" do + lambda { @daemon.start }.should raise_error(Puppet::DevError) + end + + it "should create its pidfile" do + @daemon.stubs(:agent).returns stub('agent', :start => nil) + + @daemon.expects(:create_pidfile) + @daemon.start + end + + it "should start the agent if the agent is configured" do + agent = mock 'agent' + agent.expects(:start) + @daemon.stubs(:agent).returns agent + + @daemon.start + end + + it "should start its server if one is configured" do + server = mock 'server' + server.expects(:start) + @daemon.stubs(:server).returns server + + @daemon.start + end + + it "should let the current EventLoop run" do + @daemon.stubs(:agent).returns stub('agent', :start => nil) + EventLoop.current.expects(:run) + + @daemon.start + end + end + + describe "when stopping" do + before do + @daemon.stubs(:remove_pidfile) + @daemon.stubs(:exit) + Puppet::Util::Log.stubs(:close_all) + # to make the global safe to mock, set it to a subclass of itself, + # then restore it in an after pass + without_warnings { Puppet::Application = Class.new(Puppet::Application) } + end + + after do + # restore from the superclass so we lose the stub garbage + without_warnings { Puppet::Application = Puppet::Application.superclass } + end + + it "should stop its server if one is configured" do + server = mock 'server' + server.expects(:stop) + @daemon.stubs(:server).returns server + + @daemon.stop + end + + it 'should request a stop from Puppet::Application' do + Puppet::Application.expects(:stop!) + @daemon.stop + end + + it "should remove its pidfile" do + @daemon.expects(:remove_pidfile) + + @daemon.stop + end + + it "should close all logs" do + Puppet::Util::Log.expects(:close_all) + + @daemon.stop + end + + it "should exit unless called with ':exit => false'" do + @daemon.expects(:exit) + @daemon.stop + end + + it "should not exit if called with ':exit => false'" do + @daemon.expects(:exit).never + @daemon.stop :exit => false + end + end + + describe "when creating its pidfile" do + it "should use an exclusive mutex" do + Puppet.settings.expects(:value).with(:name).returns "me" + + sync = mock 'sync' + Puppet::Util.expects(:sync).with("me").returns sync + + sync.expects(:synchronize).with(Sync::EX) + @daemon.create_pidfile + end + + it "should lock the pidfile using the Pidlock class" do + pidfile = mock 'pidfile' + + Puppet.settings.stubs(:value).with(:name).returns "eh" + Puppet.settings.expects(:value).with(:pidfile).returns "/my/file" + + Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile + + pidfile.expects(:lock).returns true + @daemon.create_pidfile + end + + it "should fail if it cannot lock" do + pidfile = mock 'pidfile' + + Puppet.settings.stubs(:value).with(:name).returns "eh" + Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" + + Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile + + pidfile.expects(:lock).returns false + + lambda { @daemon.create_pidfile }.should raise_error + end + end + + describe "when removing its pidfile" do + it "should use an exclusive mutex" do + Puppet.settings.expects(:value).with(:name).returns "me" + + sync = mock 'sync' + Puppet::Util.expects(:sync).with("me").returns sync + + sync.expects(:synchronize).with(Sync::EX) + @daemon.remove_pidfile + end + + it "should do nothing if the pidfile is not present" do + pidfile = mock 'pidfile', :locked? => false + Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile + + Puppet.settings.stubs(:value).with(:name).returns "eh" + Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" + + pidfile.expects(:unlock).never + @daemon.remove_pidfile + end + + it "should unlock the pidfile using the Pidlock class" do + pidfile = mock 'pidfile', :locked? => true + Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile + pidfile.expects(:unlock).returns true + + Puppet.settings.stubs(:value).with(:name).returns "eh" + Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" + + @daemon.remove_pidfile + end + + it "should warn if it cannot remove the pidfile" do + pidfile = mock 'pidfile', :locked? => true + Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile + pidfile.expects(:unlock).returns false + + Puppet.settings.stubs(:value).with(:name).returns "eh" + Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" + + Puppet.expects :err + @daemon.remove_pidfile + end + end + + describe "when reloading" do + it "should do nothing if no agent is configured" do + @daemon.reload + end + + it "should do nothing if the agent is running" do + agent = mock 'agent' + agent.expects(:running?).returns true + + @daemon.stubs(:agent).returns agent + + @daemon.reload + end + + it "should run the agent if one is available and it is not running" do + agent = mock 'agent' + agent.expects(:running?).returns false + agent.expects :run + + @daemon.stubs(:agent).returns agent + + @daemon.reload + end + end + + describe "when restarting" do + before do + without_warnings { Puppet::Application = Class.new(Puppet::Application) } + end + + after do + without_warnings { Puppet::Application = Puppet::Application.superclass } + end + + it 'should set Puppet::Application.restart!' do + Puppet::Application.expects(:restart!) + @daemon.stubs(:reexec) + @daemon.restart + end + + it "should reexec itself if no agent is available" do + @daemon.expects(:reexec) + + @daemon.restart + end + + it "should reexec itself if the agent is not running" do + agent = mock 'agent' + agent.expects(:running?).returns false + @daemon.stubs(:agent).returns agent + @daemon.expects(:reexec) + + @daemon.restart + end + end + + describe "when reexecing it self" do + before do + @daemon.stubs(:exec) + @daemon.stubs(:stop) + end + + it "should fail if no argv values are available" do + @daemon.expects(:argv).returns nil + lambda { @daemon.reexec }.should raise_error(Puppet::DevError) + end + + it "should shut down without exiting" do + @daemon.argv = %w{foo} + @daemon.expects(:stop).with(:exit => false) + + @daemon.reexec + end + + it "should call 'exec' with the original executable and arguments" do + @daemon.argv = %w{foo} + @daemon.expects(:exec).with($0 + " " + "foo") + + @daemon.reexec + end + end +end diff --git a/spec/unit/daemon_spec_spec.rb b/spec/unit/daemon_spec_spec.rb deleted file mode 100755 index e163f4067..000000000 --- a/spec/unit/daemon_spec_spec.rb +++ /dev/null @@ -1,306 +0,0 @@ -#!/usr/bin/env ruby" - -require File.dirname(__FILE__) + '/../spec_helper' -require 'puppet/daemon' - -def without_warnings - flag = $VERBOSE - $VERBOSE = nil - yield - $VERBOSE = flag -end - -describe Puppet::Daemon do - before do - @daemon = Puppet::Daemon.new - end - - it "should be able to manage an agent" do - @daemon.should respond_to(:agent) - end - - it "should be able to manage a network server" do - @daemon.should respond_to(:server) - end - - it "should reopen the Log logs when told to reopen logs" do - Puppet::Util::Log.expects(:reopen) - @daemon.reopen_logs - end - - describe "when setting signal traps" do - before do - @daemon.stubs(:trap) - end - - {:INT => :stop, :TERM => :stop, :HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}.each do |signal, method| - it "should log and call #{method} when it receives #{signal}" do - @daemon.expects(:trap).with(signal).yields - - Puppet.expects(:notice) - - @daemon.expects(method) - - @daemon.set_signal_traps - end - end - end - - describe "when starting" do - before do - @daemon.stubs(:create_pidfile) - @daemon.stubs(:set_signal_traps) - EventLoop.current.stubs(:run) - end - - it "should fail if it has neither agent nor server" do - lambda { @daemon.start }.should raise_error(Puppet::DevError) - end - - it "should create its pidfile" do - @daemon.stubs(:agent).returns stub('agent', :start => nil) - - @daemon.expects(:create_pidfile) - @daemon.start - end - - it "should start the agent if the agent is configured" do - agent = mock 'agent' - agent.expects(:start) - @daemon.stubs(:agent).returns agent - - @daemon.start - end - - it "should start its server if one is configured" do - server = mock 'server' - server.expects(:start) - @daemon.stubs(:server).returns server - - @daemon.start - end - - it "should let the current EventLoop run" do - @daemon.stubs(:agent).returns stub('agent', :start => nil) - EventLoop.current.expects(:run) - - @daemon.start - end - end - - describe "when stopping" do - before do - @daemon.stubs(:remove_pidfile) - @daemon.stubs(:exit) - Puppet::Util::Log.stubs(:close_all) - # to make the global safe to mock, set it to a subclass of itself, - # then restore it in an after pass - without_warnings { Puppet::Application = Class.new(Puppet::Application) } - end - - after do - # restore from the superclass so we lose the stub garbage - without_warnings { Puppet::Application = Puppet::Application.superclass } - end - - it "should stop its server if one is configured" do - server = mock 'server' - server.expects(:stop) - @daemon.stubs(:server).returns server - - @daemon.stop - end - - it 'should request a stop from Puppet::Application' do - Puppet::Application.expects(:stop!) - @daemon.stop - end - - it "should remove its pidfile" do - @daemon.expects(:remove_pidfile) - - @daemon.stop - end - - it "should close all logs" do - Puppet::Util::Log.expects(:close_all) - - @daemon.stop - end - - it "should exit unless called with ':exit => false'" do - @daemon.expects(:exit) - @daemon.stop - end - - it "should not exit if called with ':exit => false'" do - @daemon.expects(:exit).never - @daemon.stop :exit => false - end - end - - describe "when creating its pidfile" do - it "should use an exclusive mutex" do - Puppet.settings.expects(:value).with(:name).returns "me" - - sync = mock 'sync' - Puppet::Util.expects(:sync).with("me").returns sync - - sync.expects(:synchronize).with(Sync::EX) - @daemon.create_pidfile - end - - it "should lock the pidfile using the Pidlock class" do - pidfile = mock 'pidfile' - - Puppet.settings.stubs(:value).with(:name).returns "eh" - Puppet.settings.expects(:value).with(:pidfile).returns "/my/file" - - Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile - - pidfile.expects(:lock).returns true - @daemon.create_pidfile - end - - it "should fail if it cannot lock" do - pidfile = mock 'pidfile' - - Puppet.settings.stubs(:value).with(:name).returns "eh" - Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" - - Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile - - pidfile.expects(:lock).returns false - - lambda { @daemon.create_pidfile }.should raise_error - end - end - - describe "when removing its pidfile" do - it "should use an exclusive mutex" do - Puppet.settings.expects(:value).with(:name).returns "me" - - sync = mock 'sync' - Puppet::Util.expects(:sync).with("me").returns sync - - sync.expects(:synchronize).with(Sync::EX) - @daemon.remove_pidfile - end - - it "should do nothing if the pidfile is not present" do - pidfile = mock 'pidfile', :locked? => false - Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile - - Puppet.settings.stubs(:value).with(:name).returns "eh" - Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" - - pidfile.expects(:unlock).never - @daemon.remove_pidfile - end - - it "should unlock the pidfile using the Pidlock class" do - pidfile = mock 'pidfile', :locked? => true - Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile - pidfile.expects(:unlock).returns true - - Puppet.settings.stubs(:value).with(:name).returns "eh" - Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" - - @daemon.remove_pidfile - end - - it "should warn if it cannot remove the pidfile" do - pidfile = mock 'pidfile', :locked? => true - Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile - pidfile.expects(:unlock).returns false - - Puppet.settings.stubs(:value).with(:name).returns "eh" - Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" - - Puppet.expects :err - @daemon.remove_pidfile - end - end - - describe "when reloading" do - it "should do nothing if no agent is configured" do - @daemon.reload - end - - it "should do nothing if the agent is running" do - agent = mock 'agent' - agent.expects(:running?).returns true - - @daemon.stubs(:agent).returns agent - - @daemon.reload - end - - it "should run the agent if one is available and it is not running" do - agent = mock 'agent' - agent.expects(:running?).returns false - agent.expects :run - - @daemon.stubs(:agent).returns agent - - @daemon.reload - end - end - - describe "when restarting" do - before do - without_warnings { Puppet::Application = Class.new(Puppet::Application) } - end - - after do - without_warnings { Puppet::Application = Puppet::Application.superclass } - end - - it 'should set Puppet::Application.restart!' do - Puppet::Application.expects(:restart!) - @daemon.stubs(:reexec) - @daemon.restart - end - - it "should reexec itself if no agent is available" do - @daemon.expects(:reexec) - - @daemon.restart - end - - it "should reexec itself if the agent is not running" do - agent = mock 'agent' - agent.expects(:running?).returns false - @daemon.stubs(:agent).returns agent - @daemon.expects(:reexec) - - @daemon.restart - end - end - - describe "when reexecing it self" do - before do - @daemon.stubs(:exec) - @daemon.stubs(:stop) - end - - it "should fail if no argv values are available" do - @daemon.expects(:argv).returns nil - lambda { @daemon.reexec }.should raise_error(Puppet::DevError) - end - - it "should shut down without exiting" do - @daemon.argv = %w{foo} - @daemon.expects(:stop).with(:exit => false) - - @daemon.reexec - end - - it "should call 'exec' with the original executable and arguments" do - @daemon.argv = %w{foo} - @daemon.expects(:exec).with($0 + " " + "foo") - - @daemon.reexec - end - end -end diff --git a/spec/unit/dsl/resource_api_spec.rb b/spec/unit/dsl/resource_api_spec.rb new file mode 100755 index 000000000..628e8c8ae --- /dev/null +++ b/spec/unit/dsl/resource_api_spec.rb @@ -0,0 +1,181 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/dsl/resource_api' + +describe Puppet::DSL::ResourceAPI do + before do + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) + @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => "foo") + @resource = Puppet::Parser::Resource.new(:mytype, "myresource", :scope => @scope) + @api = Puppet::DSL::ResourceAPI.new(@resource, @scope, proc { }) + end + + it "should include the resource type collection helper" do + Puppet::DSL::ResourceAPI.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) + end + + it "should use the scope's environment as its environment" do + @scope.expects(:environment).returns "myenv" + @api.environment.should == "myenv" + end + + it "should be able to set all of its parameters as instance variables" do + @resource["foo"] = "myval" + @api.set_instance_variables + @api.instance_variable_get("@foo").should == "myval" + end + + describe "when calling a function" do + it "should return false if the function does not exist" do + Puppet::Parser::Functions.expects(:function).with("myfunc").returns nil + @api.call_function("myfunc", "foo").should be_false + end + + it "should use the scope the call the provided function with the provided arguments and return the results" do + scope = stub 'scope' + @api.stubs(:scope).returns scope + Puppet::Parser::Functions.expects(:function).with("myfunc").returns "myfunc_method" + + scope.expects(:myfunc_method).with("one", "two") + @api.call_function("myfunc", ["one", "two"]) + end + + it "should call 'include' when asked to call 'acquire'" do + scope = stub 'scope' + @api.stubs(:scope).returns scope + @api.stubs(:valid_type?).returns false + + scope.expects(:function_include).with("one", "two") + @api.acquire("one", "two") + end + end + + describe "when determining if a provided name is a valid type" do + it "should be valid if it's :class" do + @api.should be_valid_type(:class) + end + + it "should be valid if it's :node" do + @api.should be_valid_type(:node) + end + + it "should be valid if it's a builtin type" do + Puppet::Type.expects(:type).with(:mytype).returns "whatever" + @api.should be_valid_type(:mytype) + end + + it "should be valid if it's a defined resource type in the environment's known resource types" do + collection = stub 'collection' + @api.stubs(:known_resource_types).returns collection + collection.expects(:definition).with(:mytype).returns "whatever" + @api.should be_valid_type(:mytype) + end + + it "should not be valid unless it's a node, class, builtin type, or defined resource" do + collection = stub 'collection' + @api.stubs(:known_resource_types).returns collection + collection.expects(:definition).returns nil + Puppet::Type.expects(:type).returns nil + @api.should_not be_valid_type(:mytype) + end + end + + describe "when creating a resource" do + before do + @api.scope.stubs(:source).returns stub("source") + @api.scope.compiler.stubs(:add_resource) + @created_resource = Puppet::Parser::Resource.new("yay", "eh", :scope => @api.scope) + end + + it "should create and return a resource of the type specified" do + Puppet::Parser::Resource.expects(:new).with { |type, title, args| type == "mytype" }.returns @created_resource + @api.create_resource("mytype", "myname", {:foo => "bar"}).should == [@created_resource] + end + + it "should use the name from the first element of the provided argument array" do + Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "myname" }.returns @created_resource + @api.create_resource("mytype", "myname", {:foo => "bar"}) + end + + it "should create multiple resources if the first element of the argument array is an array" do + second_resource = Puppet::Parser::Resource.new('yay', "eh", :scope => @api.scope) + Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "first" }.returns @created_resource + Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "second" }.returns @created_resource + @api.create_resource("mytype", ["first", "second"], {:foo => "bar"}) + end + + it "should provide its scope as the scope" do + Puppet::Parser::Resource.expects(:new).with { |type, title, args| args[:scope] == @api.scope }.returns @created_resource + @api.create_resource("mytype", "myname", {:foo => "bar"}) + end + + it "should set each provided argument as a parameter on the created resource" do + result = @api.create_resource("mytype", "myname", {"foo" => "bar", "biz" => "baz"}).shift + result["foo"].should == "bar" + result["biz"].should == "baz" + end + + it "should add the resource to the scope's copmiler" do + Puppet::Parser::Resource.expects(:new).returns @created_resource + @api.scope.compiler.expects(:add_resource).with(@api.scope, @created_resource) + @api.create_resource("mytype", "myname", {:foo => "bar"}) + end + + it "should fail if the resource parameters are not a hash" do + lambda { @api.create_resource("mytype", "myname", %w{foo bar}) }.should raise_error(ArgumentError) + end + end + + describe "when an unknown method is called" do + it "should create a resource if the method name is a valid type" do + @api.expects(:valid_type?).with(:mytype).returns true + @api.expects(:create_resource).with(:mytype, "myname", {:foo => "bar"}).returns true + + @api.mytype("myname", :foo => "bar") + end + + it "should call any function whose name matches the undefined method if the name is not a valid type" do + @api.expects(:valid_type?).with(:myfunc).returns false + @api.expects(:create_resource).never + + Puppet::Parser::Functions.expects(:function).with(:myfunc).returns true + + @api.expects(:call_function).with(:myfunc, %w{foo bar}) + + @api.myfunc("foo", "bar") + end + + it "should raise a method missing error if the method is neither a type nor a function" do + @api.expects(:valid_type?).with(:myfunc).returns false + @api.expects(:create_resource).never + + Puppet::Parser::Functions.expects(:function).with(:myfunc).returns false + + @api.expects(:call_function).never + + lambda { @api.myfunc("foo", "bar") }.should raise_error(NoMethodError) + end + end + + it "should mark the specified resource as exported when creating a single exported resource" do + resources = @api.export @api.file("/my/file", :ensure => :present) + resources[0].should be_exported + end + + it "should mark all created resources as exported when creating exported resources using a block" do + @compiler.expects(:add_resource).with { |s, res| res.exported == true } + @api.export { file "/my/file", :ensure => :present } + end + + it "should mark the specified resource as virtual when creating a single virtual resource" do + resources = @api.virtual @api.file("/my/file", :ensure => :present) + resources[0].should be_virtual + end + + it "should mark all created resources as virtual when creating virtual resources using a block" do + @compiler.expects(:add_resource).with { |s, res| res.virtual == true } + @api.virtual { file "/my/file", :ensure => :present } + end +end diff --git a/spec/unit/dsl/resource_api_spec_spec.rb b/spec/unit/dsl/resource_api_spec_spec.rb deleted file mode 100755 index 628e8c8ae..000000000 --- a/spec/unit/dsl/resource_api_spec_spec.rb +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/dsl/resource_api' - -describe Puppet::DSL::ResourceAPI do - before do - @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) - @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => "foo") - @resource = Puppet::Parser::Resource.new(:mytype, "myresource", :scope => @scope) - @api = Puppet::DSL::ResourceAPI.new(@resource, @scope, proc { }) - end - - it "should include the resource type collection helper" do - Puppet::DSL::ResourceAPI.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) - end - - it "should use the scope's environment as its environment" do - @scope.expects(:environment).returns "myenv" - @api.environment.should == "myenv" - end - - it "should be able to set all of its parameters as instance variables" do - @resource["foo"] = "myval" - @api.set_instance_variables - @api.instance_variable_get("@foo").should == "myval" - end - - describe "when calling a function" do - it "should return false if the function does not exist" do - Puppet::Parser::Functions.expects(:function).with("myfunc").returns nil - @api.call_function("myfunc", "foo").should be_false - end - - it "should use the scope the call the provided function with the provided arguments and return the results" do - scope = stub 'scope' - @api.stubs(:scope).returns scope - Puppet::Parser::Functions.expects(:function).with("myfunc").returns "myfunc_method" - - scope.expects(:myfunc_method).with("one", "two") - @api.call_function("myfunc", ["one", "two"]) - end - - it "should call 'include' when asked to call 'acquire'" do - scope = stub 'scope' - @api.stubs(:scope).returns scope - @api.stubs(:valid_type?).returns false - - scope.expects(:function_include).with("one", "two") - @api.acquire("one", "two") - end - end - - describe "when determining if a provided name is a valid type" do - it "should be valid if it's :class" do - @api.should be_valid_type(:class) - end - - it "should be valid if it's :node" do - @api.should be_valid_type(:node) - end - - it "should be valid if it's a builtin type" do - Puppet::Type.expects(:type).with(:mytype).returns "whatever" - @api.should be_valid_type(:mytype) - end - - it "should be valid if it's a defined resource type in the environment's known resource types" do - collection = stub 'collection' - @api.stubs(:known_resource_types).returns collection - collection.expects(:definition).with(:mytype).returns "whatever" - @api.should be_valid_type(:mytype) - end - - it "should not be valid unless it's a node, class, builtin type, or defined resource" do - collection = stub 'collection' - @api.stubs(:known_resource_types).returns collection - collection.expects(:definition).returns nil - Puppet::Type.expects(:type).returns nil - @api.should_not be_valid_type(:mytype) - end - end - - describe "when creating a resource" do - before do - @api.scope.stubs(:source).returns stub("source") - @api.scope.compiler.stubs(:add_resource) - @created_resource = Puppet::Parser::Resource.new("yay", "eh", :scope => @api.scope) - end - - it "should create and return a resource of the type specified" do - Puppet::Parser::Resource.expects(:new).with { |type, title, args| type == "mytype" }.returns @created_resource - @api.create_resource("mytype", "myname", {:foo => "bar"}).should == [@created_resource] - end - - it "should use the name from the first element of the provided argument array" do - Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "myname" }.returns @created_resource - @api.create_resource("mytype", "myname", {:foo => "bar"}) - end - - it "should create multiple resources if the first element of the argument array is an array" do - second_resource = Puppet::Parser::Resource.new('yay', "eh", :scope => @api.scope) - Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "first" }.returns @created_resource - Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "second" }.returns @created_resource - @api.create_resource("mytype", ["first", "second"], {:foo => "bar"}) - end - - it "should provide its scope as the scope" do - Puppet::Parser::Resource.expects(:new).with { |type, title, args| args[:scope] == @api.scope }.returns @created_resource - @api.create_resource("mytype", "myname", {:foo => "bar"}) - end - - it "should set each provided argument as a parameter on the created resource" do - result = @api.create_resource("mytype", "myname", {"foo" => "bar", "biz" => "baz"}).shift - result["foo"].should == "bar" - result["biz"].should == "baz" - end - - it "should add the resource to the scope's copmiler" do - Puppet::Parser::Resource.expects(:new).returns @created_resource - @api.scope.compiler.expects(:add_resource).with(@api.scope, @created_resource) - @api.create_resource("mytype", "myname", {:foo => "bar"}) - end - - it "should fail if the resource parameters are not a hash" do - lambda { @api.create_resource("mytype", "myname", %w{foo bar}) }.should raise_error(ArgumentError) - end - end - - describe "when an unknown method is called" do - it "should create a resource if the method name is a valid type" do - @api.expects(:valid_type?).with(:mytype).returns true - @api.expects(:create_resource).with(:mytype, "myname", {:foo => "bar"}).returns true - - @api.mytype("myname", :foo => "bar") - end - - it "should call any function whose name matches the undefined method if the name is not a valid type" do - @api.expects(:valid_type?).with(:myfunc).returns false - @api.expects(:create_resource).never - - Puppet::Parser::Functions.expects(:function).with(:myfunc).returns true - - @api.expects(:call_function).with(:myfunc, %w{foo bar}) - - @api.myfunc("foo", "bar") - end - - it "should raise a method missing error if the method is neither a type nor a function" do - @api.expects(:valid_type?).with(:myfunc).returns false - @api.expects(:create_resource).never - - Puppet::Parser::Functions.expects(:function).with(:myfunc).returns false - - @api.expects(:call_function).never - - lambda { @api.myfunc("foo", "bar") }.should raise_error(NoMethodError) - end - end - - it "should mark the specified resource as exported when creating a single exported resource" do - resources = @api.export @api.file("/my/file", :ensure => :present) - resources[0].should be_exported - end - - it "should mark all created resources as exported when creating exported resources using a block" do - @compiler.expects(:add_resource).with { |s, res| res.exported == true } - @api.export { file "/my/file", :ensure => :present } - end - - it "should mark the specified resource as virtual when creating a single virtual resource" do - resources = @api.virtual @api.file("/my/file", :ensure => :present) - resources[0].should be_virtual - end - - it "should mark all created resources as virtual when creating virtual resources using a block" do - @compiler.expects(:add_resource).with { |s, res| res.virtual == true } - @api.virtual { file "/my/file", :ensure => :present } - end -end diff --git a/spec/unit/dsl/resource_type_api_spec.rb b/spec/unit/dsl/resource_type_api_spec.rb new file mode 100755 index 000000000..8d63bf28e --- /dev/null +++ b/spec/unit/dsl/resource_type_api_spec.rb @@ -0,0 +1,46 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/dsl/resource_type_api' + +class DSLAPITester + include Puppet::DSL::ResourceTypeAPI +end + +describe Puppet::DSL::ResourceTypeAPI do + before do + @api = DSLAPITester.new + end + + [:definition, :node, :hostclass].each do |type| + method = type == :definition ? "resource_type" : type + it "should be able to create a #{type}" do + newtype = Puppet::Resource::Type.new(:hostclass, "foo") + Puppet::Resource::Type.expects(:new).with { |t, n, args| t == type }.returns newtype + @api.send(method, "myname") + end + + it "should use the provided name when creating a #{type}" do + type = Puppet::Resource::Type.new(:hostclass, "foo") + Puppet::Resource::Type.expects(:new).with { |t, n, args| n == "myname" }.returns type + @api.send(method, "myname") + end + + unless type == :definition + it "should pass in any provided options" do + type = Puppet::Resource::Type.new(:hostclass, "foo") + Puppet::Resource::Type.expects(:new).with { |t, n, args| args == {:myarg => :myvalue} }.returns type + @api.send(method, "myname", :myarg => :myvalue) + end + end + + it "should set any provided block as the type's ruby code" + + it "should add the type to the current environment's known resource types" + end + + describe "when creating a definition" do + it "should use the provided options to define valid arguments for the resource type" + end +end diff --git a/spec/unit/dsl/resource_type_api_spec_spec.rb b/spec/unit/dsl/resource_type_api_spec_spec.rb deleted file mode 100755 index 8d63bf28e..000000000 --- a/spec/unit/dsl/resource_type_api_spec_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/dsl/resource_type_api' - -class DSLAPITester - include Puppet::DSL::ResourceTypeAPI -end - -describe Puppet::DSL::ResourceTypeAPI do - before do - @api = DSLAPITester.new - end - - [:definition, :node, :hostclass].each do |type| - method = type == :definition ? "resource_type" : type - it "should be able to create a #{type}" do - newtype = Puppet::Resource::Type.new(:hostclass, "foo") - Puppet::Resource::Type.expects(:new).with { |t, n, args| t == type }.returns newtype - @api.send(method, "myname") - end - - it "should use the provided name when creating a #{type}" do - type = Puppet::Resource::Type.new(:hostclass, "foo") - Puppet::Resource::Type.expects(:new).with { |t, n, args| n == "myname" }.returns type - @api.send(method, "myname") - end - - unless type == :definition - it "should pass in any provided options" do - type = Puppet::Resource::Type.new(:hostclass, "foo") - Puppet::Resource::Type.expects(:new).with { |t, n, args| args == {:myarg => :myvalue} }.returns type - @api.send(method, "myname", :myarg => :myvalue) - end - end - - it "should set any provided block as the type's ruby code" - - it "should add the type to the current environment's known resource types" - end - - describe "when creating a definition" do - it "should use the provided options to define valid arguments for the resource type" - end -end diff --git a/spec/unit/file_bucket/dipper_spec.rb b/spec/unit/file_bucket/dipper_spec.rb new file mode 100755 index 000000000..06ed16c4d --- /dev/null +++ b/spec/unit/file_bucket/dipper_spec.rb @@ -0,0 +1,110 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/file_bucket/dipper' +describe Puppet::FileBucket::Dipper do + before do + ['/my/file'].each do |x| + Puppet::FileBucket::Dipper.any_instance.stubs(:absolutize_path).with(x).returns(x) + end + end + + it "should fail in an informative way when there are failures backing up to the server" do + File.stubs(:exist?).returns true + File.stubs(:read).returns "content" + + @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") + + filemock = stub "bucketfile" + Puppet::FileBucket::File.stubs(:new).returns(filemock) + filemock.expects(:name).returns "name" + filemock.expects(:save).raises ArgumentError + + lambda { @dipper.backup("/my/file") }.should raise_error(Puppet::Error) + end + + it "should backup files to a local bucket" do + @dipper = Puppet::FileBucket::Dipper.new( + :Path => "/my/bucket" + ) + + File.stubs(:exist?).returns true + File.stubs(:read).with("/my/file").returns "my contents" + + bucketfile = stub "bucketfile" + bucketfile.stubs(:name).returns('md5/DIGEST123') + bucketfile.stubs(:checksum_data).returns("DIGEST123") + bucketfile.expects(:save).with('md5/DIGEST123') + + Puppet::FileBucket::File.stubs(:new).with( + "my contents", + :bucket_path => '/my/bucket', + :path => '/my/file' + ).returns(bucketfile) + + @dipper.backup("/my/file").should == "DIGEST123" + end + + it "should retrieve files from a local bucket" do + @dipper = Puppet::FileBucket::Dipper.new( + :Path => "/my/bucket" + ) + + File.stubs(:exist?).returns true + File.stubs(:read).with("/my/file").returns "my contents" + + bucketfile = stub "bucketfile" + bucketfile.stubs(:to_s).returns "Content" + + Puppet::FileBucket::File.expects(:find).with{|x,opts| + x == 'md5/DIGEST123' + }.returns(bucketfile) + + @dipper.getfile("DIGEST123").should == "Content" + end + + it "should backup files to a remote server" do + @dipper = Puppet::FileBucket::Dipper.new( + :Server => "puppetmaster", + :Port => "31337" + ) + + File.stubs(:exist?).returns true + File.stubs(:read).with("/my/file").returns "my contents" + + bucketfile = stub "bucketfile" + bucketfile.stubs(:name).returns('md5/DIGEST123') + bucketfile.stubs(:checksum_data).returns("DIGEST123") + bucketfile.expects(:save).with('https://puppetmaster:31337/production/file_bucket_file/md5/DIGEST123') + + Puppet::FileBucket::File.stubs(:new).with( + "my contents", + :bucket_path => nil, + :path => '/my/file' + ).returns(bucketfile) + + @dipper.backup("/my/file").should == "DIGEST123" + end + + it "should retrieve files from a remote server" do + @dipper = Puppet::FileBucket::Dipper.new( + :Server => "puppetmaster", + :Port => "31337" + ) + + File.stubs(:exist?).returns true + File.stubs(:read).with("/my/file").returns "my contents" + + bucketfile = stub "bucketfile" + bucketfile.stubs(:to_s).returns "Content" + + Puppet::FileBucket::File.expects(:find).with{|x,opts| + x == 'https://puppetmaster:31337/production/file_bucket_file/md5/DIGEST123' + }.returns(bucketfile) + + @dipper.getfile("DIGEST123").should == "Content" + end + + +end diff --git a/spec/unit/file_bucket/dipper_spec_spec.rb b/spec/unit/file_bucket/dipper_spec_spec.rb deleted file mode 100755 index 06ed16c4d..000000000 --- a/spec/unit/file_bucket/dipper_spec_spec.rb +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/file_bucket/dipper' -describe Puppet::FileBucket::Dipper do - before do - ['/my/file'].each do |x| - Puppet::FileBucket::Dipper.any_instance.stubs(:absolutize_path).with(x).returns(x) - end - end - - it "should fail in an informative way when there are failures backing up to the server" do - File.stubs(:exist?).returns true - File.stubs(:read).returns "content" - - @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") - - filemock = stub "bucketfile" - Puppet::FileBucket::File.stubs(:new).returns(filemock) - filemock.expects(:name).returns "name" - filemock.expects(:save).raises ArgumentError - - lambda { @dipper.backup("/my/file") }.should raise_error(Puppet::Error) - end - - it "should backup files to a local bucket" do - @dipper = Puppet::FileBucket::Dipper.new( - :Path => "/my/bucket" - ) - - File.stubs(:exist?).returns true - File.stubs(:read).with("/my/file").returns "my contents" - - bucketfile = stub "bucketfile" - bucketfile.stubs(:name).returns('md5/DIGEST123') - bucketfile.stubs(:checksum_data).returns("DIGEST123") - bucketfile.expects(:save).with('md5/DIGEST123') - - Puppet::FileBucket::File.stubs(:new).with( - "my contents", - :bucket_path => '/my/bucket', - :path => '/my/file' - ).returns(bucketfile) - - @dipper.backup("/my/file").should == "DIGEST123" - end - - it "should retrieve files from a local bucket" do - @dipper = Puppet::FileBucket::Dipper.new( - :Path => "/my/bucket" - ) - - File.stubs(:exist?).returns true - File.stubs(:read).with("/my/file").returns "my contents" - - bucketfile = stub "bucketfile" - bucketfile.stubs(:to_s).returns "Content" - - Puppet::FileBucket::File.expects(:find).with{|x,opts| - x == 'md5/DIGEST123' - }.returns(bucketfile) - - @dipper.getfile("DIGEST123").should == "Content" - end - - it "should backup files to a remote server" do - @dipper = Puppet::FileBucket::Dipper.new( - :Server => "puppetmaster", - :Port => "31337" - ) - - File.stubs(:exist?).returns true - File.stubs(:read).with("/my/file").returns "my contents" - - bucketfile = stub "bucketfile" - bucketfile.stubs(:name).returns('md5/DIGEST123') - bucketfile.stubs(:checksum_data).returns("DIGEST123") - bucketfile.expects(:save).with('https://puppetmaster:31337/production/file_bucket_file/md5/DIGEST123') - - Puppet::FileBucket::File.stubs(:new).with( - "my contents", - :bucket_path => nil, - :path => '/my/file' - ).returns(bucketfile) - - @dipper.backup("/my/file").should == "DIGEST123" - end - - it "should retrieve files from a remote server" do - @dipper = Puppet::FileBucket::Dipper.new( - :Server => "puppetmaster", - :Port => "31337" - ) - - File.stubs(:exist?).returns true - File.stubs(:read).with("/my/file").returns "my contents" - - bucketfile = stub "bucketfile" - bucketfile.stubs(:to_s).returns "Content" - - Puppet::FileBucket::File.expects(:find).with{|x,opts| - x == 'https://puppetmaster:31337/production/file_bucket_file/md5/DIGEST123' - }.returns(bucketfile) - - @dipper.getfile("DIGEST123").should == "Content" - end - - -end diff --git a/spec/unit/file_bucket/file_spec.rb b/spec/unit/file_bucket/file_spec.rb new file mode 100644 index 000000000..73587652c --- /dev/null +++ b/spec/unit/file_bucket/file_spec.rb @@ -0,0 +1,230 @@ +#!/usr/bin/env ruby + +require ::File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/file_bucket/file' +require 'digest/md5' +require 'digest/sha1' + +describe Puppet::FileBucket::File do + before do + # this is the default from spec_helper, but it keeps getting reset at odd times + Puppet[:bucketdir] = "/dev/null/bucket" + + @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' + + @contents = "file contents" + end + + it "should have a to_s method to return the contents" do + Puppet::FileBucket::File.new(@contents).to_s.should == @contents + end + + it "should calculate the checksum type from the passed in checksum" do + Puppet::FileBucket::File.new(@contents, :checksum => @checksum).checksum_type.should == "md5" + end + + it "should allow contents to be specified in a block" do + bucket = Puppet::FileBucket::File.new(nil) do |fb| + fb.contents = "content" + end + bucket.contents.should == "content" + end + + it "should raise an error if changing content" do + x = Puppet::FileBucket::File.new("first") + proc { x.contents = "new" }.should raise_error + end + + it "should require contents to be a string" do + proc { Puppet::FileBucket::File.new(5) }.should raise_error(ArgumentError) + end + + it "should raise an error if setting contents to a non-string" do + proc do + Puppet::FileBucket::File.new(nil) do |x| + x.contents = 5 + end + end.should raise_error(ArgumentError) + end + + it "should set the contents appropriately" do + Puppet::FileBucket::File.new(@contents).contents.should == @contents + end + + it "should default to 'md5' as the checksum algorithm if the algorithm is not in the name" do + Puppet::FileBucket::File.new(@contents).checksum_type.should == "md5" + end + + it "should calculate the checksum" do + Puppet::FileBucket::File.new(@contents).checksum.should == @checksum + end + + it "should remove the old checksum value if the algorithm is changed" do + sum = Puppet::FileBucket::File.new(@contents) + sum.checksum.should_not be_nil + + newsum = Digest::SHA1.hexdigest(@contents).to_s + sum.checksum_type = :sha1 + sum.checksum.should == "{sha1}#{newsum}" + end + + it "should support specifying the checksum_type during initialization" do + sum = Puppet::FileBucket::File.new(@contents, :checksum_type => :sha1) + sum.checksum_type.should == :sha1 + end + + it "should fail when an unsupported checksum_type is used" do + proc { Puppet::FileBucket::File.new(@contents, :checksum_type => :nope) }.should raise_error(ArgumentError) + end + + it "should fail if given an checksum at initialization that does not match the contents" do + proc { Puppet::FileBucket::File.new(@contents, :checksum => "{md5}00000000000000000000000000000000") }.should raise_error(RuntimeError) + end + + it "should fail if assigned a checksum that does not match the contents" do + bucket = Puppet::FileBucket::File.new(@contents) + proc { bucket.checksum = "{md5}00000000000000000000000000000000" }.should raise_error(RuntimeError) + end + + describe "when using back-ends" do + it "should redirect using Puppet::Indirector" do + Puppet::Indirector::Indirection.instance(:file_bucket_file).model.should equal(Puppet::FileBucket::File) + end + + it "should have a :save instance method" do + Puppet::FileBucket::File.new("mysum").should respond_to(:save) + end + + it "should respond to :find" do + Puppet::FileBucket::File.should respond_to(:find) + end + + it "should respond to :destroy" do + Puppet::FileBucket::File.should respond_to(:destroy) + end + end + + describe "when saving files" do + it "should save the contents to the calculated path" do + ::File.stubs(:directory?).with(@dir).returns(true) + ::File.expects(:exist?).with("#{@dir}/contents").returns false + + mockfile = mock "file" + mockfile.expects(:print).with(@contents) + ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440).yields(mockfile) + + Puppet::FileBucket::File.new(@contents).save + end + + it "should make any directories necessary for storage" do + FileUtils.expects(:mkdir_p).with do |arg| + ::File.umask == 0007 and arg == @dir + end + ::File.expects(:directory?).with(@dir).returns(false) + ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) + ::File.expects(:exist?).with("#{@dir}/contents").returns false + + Puppet::FileBucket::File.new(@contents).save + end + + it "should append the path to the paths file" do + remote_path = '/path/on/the/remote/box' + + ::File.expects(:directory?).with(@dir).returns(true) + ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) + ::File.expects(:exist?).with("#{@dir}/contents").returns false + + mockfile = mock "file" + mockfile.expects(:puts).with('/path/on/the/remote/box') + ::File.expects(:exist?).with("#{@dir}/paths").returns false + ::File.expects(:open).with("#{@dir}/paths", ::File::WRONLY|::File::CREAT|::File::APPEND).yields mockfile + Puppet::FileBucket::File.new(@contents, :path => remote_path).save + + end + end + + it "should accept a path" do + remote_path = '/path/on/the/remote/box' + Puppet::FileBucket::File.new(@contents, :path => remote_path).path.should == remote_path + end + + it "should return a url-ish name" do + Puppet::FileBucket::File.new(@contents).name.should == "md5/4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + end + + it "should reject a url-ish name with an invalid checksum" do + bucket = Puppet::FileBucket::File.new(@contents) + lambda { bucket.name = "sha1/4a8ec4fa5f01b4ab1a0ab8cbccb709f0/new/path" }.should raise_error + end + + it "should accept a url-ish name" do + bucket = Puppet::FileBucket::File.new(@contents) + lambda { bucket.name = "sha1/034fa2ed8e211e4d20f20e792d777f4a30af1a93/new/path" }.should_not raise_error + bucket.checksum_type.should == "sha1" + bucket.checksum_data.should == '034fa2ed8e211e4d20f20e792d777f4a30af1a93' + bucket.path.should == "new/path" + end + + it "should return a url-ish name with a path" do + Puppet::FileBucket::File.new(@contents, :path => 'my/path').name.should == "md5/4a8ec4fa5f01b4ab1a0ab8cbccb709f0/my/path" + end + + it "should convert the contents to PSON" do + Puppet::FileBucket::File.new(@contents).to_pson.should == '{"contents":"file contents"}' + end + + it "should load from PSON" do + Puppet::FileBucket::File.from_pson({"contents"=>"file contents"}).contents.should == "file contents" + end + + it "should save a file" do + ::File.expects(:exist?).with("#{@dir}/contents").returns false + ::File.expects(:directory?).with(@dir).returns false + ::FileUtils.expects(:mkdir_p).with(@dir) + ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) + + bucketfile = Puppet::FileBucket::File.new(@contents) + bucketfile.save + + end + + describe "using the indirector's find method" do + it "should return nil if a file doesn't exist" do + ::File.expects(:exist?).with("#{@dir}/contents").returns false + + bucketfile = Puppet::FileBucket::File.find("{md5}#{@digest}") + bucketfile.should == nil + end + + it "should find a filebucket if the file exists" do + ::File.expects(:exist?).with("#{@dir}/contents").returns true + ::File.expects(:exist?).with("#{@dir}/paths").returns false + ::File.expects(:read).with("#{@dir}/contents").returns @contents + + bucketfile = Puppet::FileBucket::File.find("{md5}#{@digest}") + bucketfile.should_not == nil + end + + describe "using RESTish digest notation" do + it "should return nil if a file doesn't exist" do + ::File.expects(:exist?).with("#{@dir}/contents").returns false + + bucketfile = Puppet::FileBucket::File.find("md5/#{@digest}") + bucketfile.should == nil + end + + it "should find a filebucket if the file exists" do + ::File.expects(:exist?).with("#{@dir}/contents").returns true + ::File.expects(:exist?).with("#{@dir}/paths").returns false + ::File.expects(:read).with("#{@dir}/contents").returns @contents + + bucketfile = Puppet::FileBucket::File.find("md5/#{@digest}") + bucketfile.should_not == nil + end + + end + end +end diff --git a/spec/unit/file_bucket/file_spec_spec.rb b/spec/unit/file_bucket/file_spec_spec.rb deleted file mode 100644 index 73587652c..000000000 --- a/spec/unit/file_bucket/file_spec_spec.rb +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env ruby - -require ::File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/file_bucket/file' -require 'digest/md5' -require 'digest/sha1' - -describe Puppet::FileBucket::File do - before do - # this is the default from spec_helper, but it keeps getting reset at odd times - Puppet[:bucketdir] = "/dev/null/bucket" - - @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' - - @contents = "file contents" - end - - it "should have a to_s method to return the contents" do - Puppet::FileBucket::File.new(@contents).to_s.should == @contents - end - - it "should calculate the checksum type from the passed in checksum" do - Puppet::FileBucket::File.new(@contents, :checksum => @checksum).checksum_type.should == "md5" - end - - it "should allow contents to be specified in a block" do - bucket = Puppet::FileBucket::File.new(nil) do |fb| - fb.contents = "content" - end - bucket.contents.should == "content" - end - - it "should raise an error if changing content" do - x = Puppet::FileBucket::File.new("first") - proc { x.contents = "new" }.should raise_error - end - - it "should require contents to be a string" do - proc { Puppet::FileBucket::File.new(5) }.should raise_error(ArgumentError) - end - - it "should raise an error if setting contents to a non-string" do - proc do - Puppet::FileBucket::File.new(nil) do |x| - x.contents = 5 - end - end.should raise_error(ArgumentError) - end - - it "should set the contents appropriately" do - Puppet::FileBucket::File.new(@contents).contents.should == @contents - end - - it "should default to 'md5' as the checksum algorithm if the algorithm is not in the name" do - Puppet::FileBucket::File.new(@contents).checksum_type.should == "md5" - end - - it "should calculate the checksum" do - Puppet::FileBucket::File.new(@contents).checksum.should == @checksum - end - - it "should remove the old checksum value if the algorithm is changed" do - sum = Puppet::FileBucket::File.new(@contents) - sum.checksum.should_not be_nil - - newsum = Digest::SHA1.hexdigest(@contents).to_s - sum.checksum_type = :sha1 - sum.checksum.should == "{sha1}#{newsum}" - end - - it "should support specifying the checksum_type during initialization" do - sum = Puppet::FileBucket::File.new(@contents, :checksum_type => :sha1) - sum.checksum_type.should == :sha1 - end - - it "should fail when an unsupported checksum_type is used" do - proc { Puppet::FileBucket::File.new(@contents, :checksum_type => :nope) }.should raise_error(ArgumentError) - end - - it "should fail if given an checksum at initialization that does not match the contents" do - proc { Puppet::FileBucket::File.new(@contents, :checksum => "{md5}00000000000000000000000000000000") }.should raise_error(RuntimeError) - end - - it "should fail if assigned a checksum that does not match the contents" do - bucket = Puppet::FileBucket::File.new(@contents) - proc { bucket.checksum = "{md5}00000000000000000000000000000000" }.should raise_error(RuntimeError) - end - - describe "when using back-ends" do - it "should redirect using Puppet::Indirector" do - Puppet::Indirector::Indirection.instance(:file_bucket_file).model.should equal(Puppet::FileBucket::File) - end - - it "should have a :save instance method" do - Puppet::FileBucket::File.new("mysum").should respond_to(:save) - end - - it "should respond to :find" do - Puppet::FileBucket::File.should respond_to(:find) - end - - it "should respond to :destroy" do - Puppet::FileBucket::File.should respond_to(:destroy) - end - end - - describe "when saving files" do - it "should save the contents to the calculated path" do - ::File.stubs(:directory?).with(@dir).returns(true) - ::File.expects(:exist?).with("#{@dir}/contents").returns false - - mockfile = mock "file" - mockfile.expects(:print).with(@contents) - ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440).yields(mockfile) - - Puppet::FileBucket::File.new(@contents).save - end - - it "should make any directories necessary for storage" do - FileUtils.expects(:mkdir_p).with do |arg| - ::File.umask == 0007 and arg == @dir - end - ::File.expects(:directory?).with(@dir).returns(false) - ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) - ::File.expects(:exist?).with("#{@dir}/contents").returns false - - Puppet::FileBucket::File.new(@contents).save - end - - it "should append the path to the paths file" do - remote_path = '/path/on/the/remote/box' - - ::File.expects(:directory?).with(@dir).returns(true) - ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) - ::File.expects(:exist?).with("#{@dir}/contents").returns false - - mockfile = mock "file" - mockfile.expects(:puts).with('/path/on/the/remote/box') - ::File.expects(:exist?).with("#{@dir}/paths").returns false - ::File.expects(:open).with("#{@dir}/paths", ::File::WRONLY|::File::CREAT|::File::APPEND).yields mockfile - Puppet::FileBucket::File.new(@contents, :path => remote_path).save - - end - end - - it "should accept a path" do - remote_path = '/path/on/the/remote/box' - Puppet::FileBucket::File.new(@contents, :path => remote_path).path.should == remote_path - end - - it "should return a url-ish name" do - Puppet::FileBucket::File.new(@contents).name.should == "md5/4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - end - - it "should reject a url-ish name with an invalid checksum" do - bucket = Puppet::FileBucket::File.new(@contents) - lambda { bucket.name = "sha1/4a8ec4fa5f01b4ab1a0ab8cbccb709f0/new/path" }.should raise_error - end - - it "should accept a url-ish name" do - bucket = Puppet::FileBucket::File.new(@contents) - lambda { bucket.name = "sha1/034fa2ed8e211e4d20f20e792d777f4a30af1a93/new/path" }.should_not raise_error - bucket.checksum_type.should == "sha1" - bucket.checksum_data.should == '034fa2ed8e211e4d20f20e792d777f4a30af1a93' - bucket.path.should == "new/path" - end - - it "should return a url-ish name with a path" do - Puppet::FileBucket::File.new(@contents, :path => 'my/path').name.should == "md5/4a8ec4fa5f01b4ab1a0ab8cbccb709f0/my/path" - end - - it "should convert the contents to PSON" do - Puppet::FileBucket::File.new(@contents).to_pson.should == '{"contents":"file contents"}' - end - - it "should load from PSON" do - Puppet::FileBucket::File.from_pson({"contents"=>"file contents"}).contents.should == "file contents" - end - - it "should save a file" do - ::File.expects(:exist?).with("#{@dir}/contents").returns false - ::File.expects(:directory?).with(@dir).returns false - ::FileUtils.expects(:mkdir_p).with(@dir) - ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) - - bucketfile = Puppet::FileBucket::File.new(@contents) - bucketfile.save - - end - - describe "using the indirector's find method" do - it "should return nil if a file doesn't exist" do - ::File.expects(:exist?).with("#{@dir}/contents").returns false - - bucketfile = Puppet::FileBucket::File.find("{md5}#{@digest}") - bucketfile.should == nil - end - - it "should find a filebucket if the file exists" do - ::File.expects(:exist?).with("#{@dir}/contents").returns true - ::File.expects(:exist?).with("#{@dir}/paths").returns false - ::File.expects(:read).with("#{@dir}/contents").returns @contents - - bucketfile = Puppet::FileBucket::File.find("{md5}#{@digest}") - bucketfile.should_not == nil - end - - describe "using RESTish digest notation" do - it "should return nil if a file doesn't exist" do - ::File.expects(:exist?).with("#{@dir}/contents").returns false - - bucketfile = Puppet::FileBucket::File.find("md5/#{@digest}") - bucketfile.should == nil - end - - it "should find a filebucket if the file exists" do - ::File.expects(:exist?).with("#{@dir}/contents").returns true - ::File.expects(:exist?).with("#{@dir}/paths").returns false - ::File.expects(:read).with("#{@dir}/contents").returns @contents - - bucketfile = Puppet::FileBucket::File.find("md5/#{@digest}") - bucketfile.should_not == nil - end - - end - end -end diff --git a/spec/unit/file_collection/lookup_spec.rb b/spec/unit/file_collection/lookup_spec.rb new file mode 100755 index 000000000..81cc61872 --- /dev/null +++ b/spec/unit/file_collection/lookup_spec.rb @@ -0,0 +1,46 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/file_collection/lookup' + +class LookupTester + include Puppet::FileCollection::Lookup +end + +describe Puppet::FileCollection::Lookup do + before do + @tester = LookupTester.new + + @file_collection = mock 'file_collection' + Puppet::FileCollection.stubs(:collection).returns @file_collection + end + + it "should use the file collection to determine the index of the file name" do + @file_collection.expects(:index).with("/my/file").returns 50 + + @tester.file = "/my/file" + @tester.file_index.should == 50 + end + + it "should return nil as the file name if no index is set" do + @tester.file.should be_nil + end + + it "should use the file collection to convert the index to a file name" do + @file_collection.expects(:path).with(25).returns "/path/to/file" + + @tester.file_index = 25 + + @tester.file.should == "/path/to/file" + end + + it "should support a line attribute" do + @tester.line = 50 + @tester.line.should == 50 + end + + it "should default to the global file collection" do + Puppet::FileCollection.expects(:collection).returns "collection" + @tester.file_collection.should == "collection" + end +end diff --git a/spec/unit/file_collection/lookup_spec_spec.rb b/spec/unit/file_collection/lookup_spec_spec.rb deleted file mode 100755 index 81cc61872..000000000 --- a/spec/unit/file_collection/lookup_spec_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/file_collection/lookup' - -class LookupTester - include Puppet::FileCollection::Lookup -end - -describe Puppet::FileCollection::Lookup do - before do - @tester = LookupTester.new - - @file_collection = mock 'file_collection' - Puppet::FileCollection.stubs(:collection).returns @file_collection - end - - it "should use the file collection to determine the index of the file name" do - @file_collection.expects(:index).with("/my/file").returns 50 - - @tester.file = "/my/file" - @tester.file_index.should == 50 - end - - it "should return nil as the file name if no index is set" do - @tester.file.should be_nil - end - - it "should use the file collection to convert the index to a file name" do - @file_collection.expects(:path).with(25).returns "/path/to/file" - - @tester.file_index = 25 - - @tester.file.should == "/path/to/file" - end - - it "should support a line attribute" do - @tester.line = 50 - @tester.line.should == 50 - end - - it "should default to the global file collection" do - Puppet::FileCollection.expects(:collection).returns "collection" - @tester.file_collection.should == "collection" - end -end diff --git a/spec/unit/file_collection_spec.rb b/spec/unit/file_collection_spec.rb new file mode 100755 index 000000000..81bcc2d2d --- /dev/null +++ b/spec/unit/file_collection_spec.rb @@ -0,0 +1,53 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'puppet/file_collection' + +describe Puppet::FileCollection do + before do + @collection = Puppet::FileCollection.new + end + + it "should be able to convert a file name into an index" do + @collection.index("/my/file").should be_instance_of(Fixnum) + end + + it "should be able to convert an index into a file name" do + index = @collection.index("/path/to/file") + @collection.path(index).should == "/path/to/file" + end + + it "should always give the same file name for a given index" do + index = @collection.index("/path/to/file") + @collection.path(index).should == @collection.path(index) + end + + it "should always give the same index for a given file name" do + @collection.index("/my/file").should == @collection.index("/my/file") + end + + it "should always correctly relate a file name and its index even when multiple files are in the collection" do + indexes = %w{a b c d e f}.inject({}) do |hash, letter| + hash[letter] = @collection.index("/path/to/file/%s" % letter) + hash + end + + indexes.each do |letter, index| + @collection.index("/path/to/file/%s" % letter).should == indexes[letter] + @collection.path(index).should == @collection.path(index) + end + end + + it "should return nil as the file name when an unknown index is provided" do + @collection.path(50).should be_nil + end + + it "should provide a global collection" do + Puppet::FileCollection.collection.should be_instance_of(Puppet::FileCollection) + end + + it "should reuse the global collection" do + Puppet::FileCollection.collection.should equal(Puppet::FileCollection.collection) + end +end diff --git a/spec/unit/file_collection_spec_spec.rb b/spec/unit/file_collection_spec_spec.rb deleted file mode 100755 index 81bcc2d2d..000000000 --- a/spec/unit/file_collection_spec_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -require 'puppet/file_collection' - -describe Puppet::FileCollection do - before do - @collection = Puppet::FileCollection.new - end - - it "should be able to convert a file name into an index" do - @collection.index("/my/file").should be_instance_of(Fixnum) - end - - it "should be able to convert an index into a file name" do - index = @collection.index("/path/to/file") - @collection.path(index).should == "/path/to/file" - end - - it "should always give the same file name for a given index" do - index = @collection.index("/path/to/file") - @collection.path(index).should == @collection.path(index) - end - - it "should always give the same index for a given file name" do - @collection.index("/my/file").should == @collection.index("/my/file") - end - - it "should always correctly relate a file name and its index even when multiple files are in the collection" do - indexes = %w{a b c d e f}.inject({}) do |hash, letter| - hash[letter] = @collection.index("/path/to/file/%s" % letter) - hash - end - - indexes.each do |letter, index| - @collection.index("/path/to/file/%s" % letter).should == indexes[letter] - @collection.path(index).should == @collection.path(index) - end - end - - it "should return nil as the file name when an unknown index is provided" do - @collection.path(50).should be_nil - end - - it "should provide a global collection" do - Puppet::FileCollection.collection.should be_instance_of(Puppet::FileCollection) - end - - it "should reuse the global collection" do - Puppet::FileCollection.collection.should equal(Puppet::FileCollection.collection) - end -end diff --git a/spec/unit/file_serving/base_spec.rb b/spec/unit/file_serving/base_spec.rb new file mode 100755 index 000000000..de7ea4756 --- /dev/null +++ b/spec/unit/file_serving/base_spec.rb @@ -0,0 +1,132 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/file_serving/base' + +describe Puppet::FileServing::Base do + it "should accept a path" do + Puppet::FileServing::Base.new("/module/dir/file").path.should == "/module/dir/file" + end + + it "should require that paths be fully qualified" do + lambda { Puppet::FileServing::Base.new("module/dir/file") }.should raise_error(ArgumentError) + end + + it "should allow specification of whether links should be managed" do + Puppet::FileServing::Base.new("/module/dir/file", :links => :manage).links.should == :manage + end + + it "should have a :source attribute" do + file = Puppet::FileServing::Base.new("/module/dir/file") + file.should respond_to(:source) + file.should respond_to(:source=) + end + + it "should consider :ignore links equivalent to :manage links" do + Puppet::FileServing::Base.new("/module/dir/file", :links => :ignore).links.should == :manage + end + + it "should fail if :links is set to anything other than :manage, :follow, or :ignore" do + proc { Puppet::FileServing::Base.new("/module/dir/file", :links => :else) }.should raise_error(ArgumentError) + end + + it "should allow links values to be set as strings" do + Puppet::FileServing::Base.new("/module/dir/file", :links => "follow").links.should == :follow + end + + it "should default to :manage for :links" do + Puppet::FileServing::Base.new("/module/dir/file").links.should == :manage + end + + it "should allow specification of a path" do + FileTest.stubs(:exists?).returns(true) + Puppet::FileServing::Base.new("/module/dir/file", :path => "/my/file").path.should == "/my/file" + end + + it "should allow specification of a relative path" do + FileTest.stubs(:exists?).returns(true) + Puppet::FileServing::Base.new("/module/dir/file", :relative_path => "my/file").relative_path.should == "my/file" + end + + it "should have a means of determining if the file exists" do + Puppet::FileServing::Base.new("/blah").should respond_to(:exist?) + end + + it "should correctly indicate if the file is present" do + File.expects(:lstat).with("/my/file").returns(mock("stat")) + Puppet::FileServing::Base.new("/my/file").exist?.should be_true + end + + it "should correctly indicate if the file is absent" do + File.expects(:lstat).with("/my/file").raises RuntimeError + Puppet::FileServing::Base.new("/my/file").exist?.should be_false + end + + describe "when setting the relative path" do + it "should require that the relative path be unqualified" do + @file = Puppet::FileServing::Base.new("/module/dir/file") + FileTest.stubs(:exists?).returns(true) + proc { @file.relative_path = "/qualified/file" }.should raise_error(ArgumentError) + end + end + + describe "when determining the full file path" do + before do + @file = Puppet::FileServing::Base.new("/this/file") + end + + it "should return the path if there is no relative path" do + @file.full_path.should == "/this/file" + end + + it "should return the path if the relative_path is set to ''" do + @file.relative_path = "" + @file.full_path.should == "/this/file" + end + + it "should return the path if the relative_path is set to '.'" do + @file.relative_path = "." + @file.full_path.should == "/this/file" + end + + it "should return the path joined with the relative path if there is a relative path and it is not set to '/' or ''" do + @file.relative_path = "not/qualified" + @file.full_path.should == "/this/file/not/qualified" + end + + it "should strip extra slashes" do + file = Puppet::FileServing::Base.new("//this//file") + file.full_path.should == "/this/file" + end + end + + describe "when stat'ing files" do + before do + @file = Puppet::FileServing::Base.new("/this/file") + end + + it "should stat the file's full path" do + @file.stubs(:full_path).returns("/this/file") + File.expects(:lstat).with("/this/file").returns stub("stat", :ftype => "file") + @file.stat + end + + it "should fail if the file does not exist" do + @file.stubs(:full_path).returns("/this/file") + File.expects(:lstat).with("/this/file").raises(Errno::ENOENT) + proc { @file.stat }.should raise_error(Errno::ENOENT) + end + + it "should use :lstat if :links is set to :manage" do + File.expects(:lstat).with("/this/file").returns stub("stat", :ftype => "file") + @file.stat + end + + it "should use :stat if :links is set to :follow" do + File.expects(:stat).with("/this/file").returns stub("stat", :ftype => "file") + @file.links = :follow + @file.stat + end + end +end diff --git a/spec/unit/file_serving/base_spec_spec.rb b/spec/unit/file_serving/base_spec_spec.rb deleted file mode 100755 index de7ea4756..000000000 --- a/spec/unit/file_serving/base_spec_spec.rb +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/file_serving/base' - -describe Puppet::FileServing::Base do - it "should accept a path" do - Puppet::FileServing::Base.new("/module/dir/file").path.should == "/module/dir/file" - end - - it "should require that paths be fully qualified" do - lambda { Puppet::FileServing::Base.new("module/dir/file") }.should raise_error(ArgumentError) - end - - it "should allow specification of whether links should be managed" do - Puppet::FileServing::Base.new("/module/dir/file", :links => :manage).links.should == :manage - end - - it "should have a :source attribute" do - file = Puppet::FileServing::Base.new("/module/dir/file") - file.should respond_to(:source) - file.should respond_to(:source=) - end - - it "should consider :ignore links equivalent to :manage links" do - Puppet::FileServing::Base.new("/module/dir/file", :links => :ignore).links.should == :manage - end - - it "should fail if :links is set to anything other than :manage, :follow, or :ignore" do - proc { Puppet::FileServing::Base.new("/module/dir/file", :links => :else) }.should raise_error(ArgumentError) - end - - it "should allow links values to be set as strings" do - Puppet::FileServing::Base.new("/module/dir/file", :links => "follow").links.should == :follow - end - - it "should default to :manage for :links" do - Puppet::FileServing::Base.new("/module/dir/file").links.should == :manage - end - - it "should allow specification of a path" do - FileTest.stubs(:exists?).returns(true) - Puppet::FileServing::Base.new("/module/dir/file", :path => "/my/file").path.should == "/my/file" - end - - it "should allow specification of a relative path" do - FileTest.stubs(:exists?).returns(true) - Puppet::FileServing::Base.new("/module/dir/file", :relative_path => "my/file").relative_path.should == "my/file" - end - - it "should have a means of determining if the file exists" do - Puppet::FileServing::Base.new("/blah").should respond_to(:exist?) - end - - it "should correctly indicate if the file is present" do - File.expects(:lstat).with("/my/file").returns(mock("stat")) - Puppet::FileServing::Base.new("/my/file").exist?.should be_true - end - - it "should correctly indicate if the file is absent" do - File.expects(:lstat).with("/my/file").raises RuntimeError - Puppet::FileServing::Base.new("/my/file").exist?.should be_false - end - - describe "when setting the relative path" do - it "should require that the relative path be unqualified" do - @file = Puppet::FileServing::Base.new("/module/dir/file") - FileTest.stubs(:exists?).returns(true) - proc { @file.relative_path = "/qualified/file" }.should raise_error(ArgumentError) - end - end - - describe "when determining the full file path" do - before do - @file = Puppet::FileServing::Base.new("/this/file") - end - - it "should return the path if there is no relative path" do - @file.full_path.should == "/this/file" - end - - it "should return the path if the relative_path is set to ''" do - @file.relative_path = "" - @file.full_path.should == "/this/file" - end - - it "should return the path if the relative_path is set to '.'" do - @file.relative_path = "." - @file.full_path.should == "/this/file" - end - - it "should return the path joined with the relative path if there is a relative path and it is not set to '/' or ''" do - @file.relative_path = "not/qualified" - @file.full_path.should == "/this/file/not/qualified" - end - - it "should strip extra slashes" do - file = Puppet::FileServing::Base.new("//this//file") - file.full_path.should == "/this/file" - end - end - - describe "when stat'ing files" do - before do - @file = Puppet::FileServing::Base.new("/this/file") - end - - it "should stat the file's full path" do - @file.stubs(:full_path).returns("/this/file") - File.expects(:lstat).with("/this/file").returns stub("stat", :ftype => "file") - @file.stat - end - - it "should fail if the file does not exist" do - @file.stubs(:full_path).returns("/this/file") - File.expects(:lstat).with("/this/file").raises(Errno::ENOENT) - proc { @file.stat }.should raise_error(Errno::ENOENT) - end - - it "should use :lstat if :links is set to :manage" do - File.expects(:lstat).with("/this/file").returns stub("stat", :ftype => "file") - @file.stat - end - - it "should use :stat if :links is set to :follow" do - File.expects(:stat).with("/this/file").returns stub("stat", :ftype => "file") - @file.links = :follow - @file.stat - end - end -end diff --git a/spec/unit/file_serving/configuration/parser_spec.rb b/spec/unit/file_serving/configuration/parser_spec.rb new file mode 100755 index 000000000..6faf81b4c --- /dev/null +++ b/spec/unit/file_serving/configuration/parser_spec.rb @@ -0,0 +1,181 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/file_serving/configuration/parser' + +describe Puppet::FileServing::Configuration::Parser do + it "should subclass the LoadedFile class" do + Puppet::FileServing::Configuration::Parser.superclass.should equal(Puppet::Util::LoadedFile) + end +end + + +module FSConfigurationParserTesting + def mock_file_content(content) + # We want an array, but we actually want our carriage returns on all of it. + lines = content.split("\n").collect { |l| l + "\n" } + @filehandle.stubs(:each).multiple_yields(*lines) + end +end + +describe Puppet::FileServing::Configuration::Parser do + before :each do + @path = "/my/config.conf" + FileTest.stubs(:exists?).with(@path).returns(true) + FileTest.stubs(:readable?).with(@path).returns(true) + @filehandle = mock 'filehandle' + File.expects(:open).with(@path).yields(@filehandle) + @parser = Puppet::FileServing::Configuration::Parser.new(@path) + end + + describe Puppet::FileServing::Configuration::Parser, " when parsing" do + include FSConfigurationParserTesting + + it "should allow comments" do + @filehandle.expects(:each).yields("# this is a comment\n") + proc { @parser.parse }.should_not raise_error + end + + it "should allow blank lines" do + @filehandle.expects(:each).yields("\n") + proc { @parser.parse }.should_not raise_error + end + + it "should create a new mount for each section in the configuration" do + mount1 = mock 'one', :validate => true + mount2 = mock 'two', :validate => true + Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) + Puppet::FileServing::Mount::File.expects(:new).with("two").returns(mount2) + mock_file_content "[one]\n[two]\n" + @parser.parse + end + + # This test is almost the exact same as the previous one. + it "should return a hash of the created mounts" do + mount1 = mock 'one', :validate => true + mount2 = mock 'two', :validate => true + Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) + Puppet::FileServing::Mount::File.expects(:new).with("two").returns(mount2) + mock_file_content "[one]\n[two]\n" + + result = @parser.parse + result["one"].should equal(mount1) + result["two"].should equal(mount2) + end + + it "should only allow mount names that are alphanumeric plus dashes" do + mock_file_content "[a*b]\n" + proc { @parser.parse }.should raise_error(ArgumentError) + end + + it "should fail if the value for path/allow/deny starts with an equals sign" do + mock_file_content "[one]\npath = /testing" + proc { @parser.parse }.should raise_error(ArgumentError) + end + + it "should validate each created mount" do + mount1 = mock 'one' + Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) + mock_file_content "[one]\n" + + mount1.expects(:validate) + + @parser.parse + end + + it "should fail if any mount does not pass validation" do + mount1 = mock 'one' + Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) + mock_file_content "[one]\n" + + mount1.expects(:validate).raises RuntimeError + + lambda { @parser.parse }.should raise_error(RuntimeError) + end + end + + describe Puppet::FileServing::Configuration::Parser, " when parsing mount attributes" do + include FSConfigurationParserTesting + + before do + @mount = stub 'testmount', :name => "one", :validate => true + Puppet::FileServing::Mount::File.expects(:new).with("one").returns(@mount) + @parser.stubs(:add_modules_mount) + end + + it "should set the mount path to the path attribute from that section" do + mock_file_content "[one]\npath /some/path\n" + + @mount.expects(:path=).with("/some/path") + @parser.parse + end + + it "should tell the mount to allow any allow values from the section" do + mock_file_content "[one]\nallow something\n" + + @mount.expects(:info) + @mount.expects(:allow).with("something") + @parser.parse + end + + it "should tell the mount to deny any deny values from the section" do + mock_file_content "[one]\ndeny something\n" + + @mount.expects(:info) + @mount.expects(:deny).with("something") + @parser.parse + end + + it "should fail on any attributes other than path, allow, and deny" do + mock_file_content "[one]\ndo something\n" + + proc { @parser.parse }.should raise_error(ArgumentError) + end + end + + describe Puppet::FileServing::Configuration::Parser, " when parsing the modules mount" do + include FSConfigurationParserTesting + + before do + @mount = stub 'modulesmount', :name => "modules", :validate => true + end + + it "should create an instance of the Modules Mount class" do + mock_file_content "[modules]\n" + + Puppet::FileServing::Mount::Modules.expects(:new).with("modules").returns @mount + @parser.parse + end + + it "should warn if a path is set" do + mock_file_content "[modules]\npath /some/path\n" + Puppet::FileServing::Mount::Modules.expects(:new).with("modules").returns(@mount) + + Puppet.expects(:warning) + @parser.parse + end + end + + describe Puppet::FileServing::Configuration::Parser, " when parsing the plugins mount" do + include FSConfigurationParserTesting + + before do + @mount = stub 'pluginsmount', :name => "plugins", :validate => true + end + + it "should create an instance of the Plugins Mount class" do + mock_file_content "[plugins]\n" + + Puppet::FileServing::Mount::Plugins.expects(:new).with("plugins").returns @mount + @parser.parse + end + + it "should warn if a path is set" do + mock_file_content "[plugins]\npath /some/path\n" + + Puppet.expects(:warning) + @parser.parse + end + end +end diff --git a/spec/unit/file_serving/configuration/parser_spec_spec.rb b/spec/unit/file_serving/configuration/parser_spec_spec.rb deleted file mode 100755 index 6faf81b4c..000000000 --- a/spec/unit/file_serving/configuration/parser_spec_spec.rb +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/file_serving/configuration/parser' - -describe Puppet::FileServing::Configuration::Parser do - it "should subclass the LoadedFile class" do - Puppet::FileServing::Configuration::Parser.superclass.should equal(Puppet::Util::LoadedFile) - end -end - - -module FSConfigurationParserTesting - def mock_file_content(content) - # We want an array, but we actually want our carriage returns on all of it. - lines = content.split("\n").collect { |l| l + "\n" } - @filehandle.stubs(:each).multiple_yields(*lines) - end -end - -describe Puppet::FileServing::Configuration::Parser do - before :each do - @path = "/my/config.conf" - FileTest.stubs(:exists?).with(@path).returns(true) - FileTest.stubs(:readable?).with(@path).returns(true) - @filehandle = mock 'filehandle' - File.expects(:open).with(@path).yields(@filehandle) - @parser = Puppet::FileServing::Configuration::Parser.new(@path) - end - - describe Puppet::FileServing::Configuration::Parser, " when parsing" do - include FSConfigurationParserTesting - - it "should allow comments" do - @filehandle.expects(:each).yields("# this is a comment\n") - proc { @parser.parse }.should_not raise_error - end - - it "should allow blank lines" do - @filehandle.expects(:each).yields("\n") - proc { @parser.parse }.should_not raise_error - end - - it "should create a new mount for each section in the configuration" do - mount1 = mock 'one', :validate => true - mount2 = mock 'two', :validate => true - Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) - Puppet::FileServing::Mount::File.expects(:new).with("two").returns(mount2) - mock_file_content "[one]\n[two]\n" - @parser.parse - end - - # This test is almost the exact same as the previous one. - it "should return a hash of the created mounts" do - mount1 = mock 'one', :validate => true - mount2 = mock 'two', :validate => true - Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) - Puppet::FileServing::Mount::File.expects(:new).with("two").returns(mount2) - mock_file_content "[one]\n[two]\n" - - result = @parser.parse - result["one"].should equal(mount1) - result["two"].should equal(mount2) - end - - it "should only allow mount names that are alphanumeric plus dashes" do - mock_file_content "[a*b]\n" - proc { @parser.parse }.should raise_error(ArgumentError) - end - - it "should fail if the value for path/allow/deny starts with an equals sign" do - mock_file_content "[one]\npath = /testing" - proc { @parser.parse }.should raise_error(ArgumentError) - end - - it "should validate each created mount" do - mount1 = mock 'one' - Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) - mock_file_content "[one]\n" - - mount1.expects(:validate) - - @parser.parse - end - - it "should fail if any mount does not pass validation" do - mount1 = mock 'one' - Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) - mock_file_content "[one]\n" - - mount1.expects(:validate).raises RuntimeError - - lambda { @parser.parse }.should raise_error(RuntimeError) - end - end - - describe Puppet::FileServing::Configuration::Parser, " when parsing mount attributes" do - include FSConfigurationParserTesting - - before do - @mount = stub 'testmount', :name => "one", :validate => true - Puppet::FileServing::Mount::File.expects(:new).with("one").returns(@mount) - @parser.stubs(:add_modules_mount) - end - - it "should set the mount path to the path attribute from that section" do - mock_file_content "[one]\npath /some/path\n" - - @mount.expects(:path=).with("/some/path") - @parser.parse - end - - it "should tell the mount to allow any allow values from the section" do - mock_file_content "[one]\nallow something\n" - - @mount.expects(:info) - @mount.expects(:allow).with("something") - @parser.parse - end - - it "should tell the mount to deny any deny values from the section" do - mock_file_content "[one]\ndeny something\n" - - @mount.expects(:info) - @mount.expects(:deny).with("something") - @parser.parse - end - - it "should fail on any attributes other than path, allow, and deny" do - mock_file_content "[one]\ndo something\n" - - proc { @parser.parse }.should raise_error(ArgumentError) - end - end - - describe Puppet::FileServing::Configuration::Parser, " when parsing the modules mount" do - include FSConfigurationParserTesting - - before do - @mount = stub 'modulesmount', :name => "modules", :validate => true - end - - it "should create an instance of the Modules Mount class" do - mock_file_content "[modules]\n" - - Puppet::FileServing::Mount::Modules.expects(:new).with("modules").returns @mount - @parser.parse - end - - it "should warn if a path is set" do - mock_file_content "[modules]\npath /some/path\n" - Puppet::FileServing::Mount::Modules.expects(:new).with("modules").returns(@mount) - - Puppet.expects(:warning) - @parser.parse - end - end - - describe Puppet::FileServing::Configuration::Parser, " when parsing the plugins mount" do - include FSConfigurationParserTesting - - before do - @mount = stub 'pluginsmount', :name => "plugins", :validate => true - end - - it "should create an instance of the Plugins Mount class" do - mock_file_content "[plugins]\n" - - Puppet::FileServing::Mount::Plugins.expects(:new).with("plugins").returns @mount - @parser.parse - end - - it "should warn if a path is set" do - mock_file_content "[plugins]\npath /some/path\n" - - Puppet.expects(:warning) - @parser.parse - end - end -end diff --git a/spec/unit/file_serving/configuration_spec.rb b/spec/unit/file_serving/configuration_spec.rb new file mode 100755 index 000000000..4621a0c82 --- /dev/null +++ b/spec/unit/file_serving/configuration_spec.rb @@ -0,0 +1,249 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/file_serving/configuration' + +describe Puppet::FileServing::Configuration do + it "should make :new a private method" do + proc { Puppet::FileServing::Configuration.new }.should raise_error + end + + it "should return the same configuration each time :create is called" do + Puppet::FileServing::Configuration.create.should equal(Puppet::FileServing::Configuration.create) + end + + it "should have a method for removing the current configuration instance" do + old = Puppet::FileServing::Configuration.create + Puppet::Util::Cacher.expire + Puppet::FileServing::Configuration.create.should_not equal(old) + end + + after do + Puppet::Util::Cacher.expire + end +end + +describe Puppet::FileServing::Configuration do + + before :each do + @path = "/path/to/configuration/file.conf" + Puppet.settings.stubs(:value).with(:trace).returns(false) + Puppet.settings.stubs(:value).with(:fileserverconfig).returns(@path) + end + + after :each do + Puppet::Util::Cacher.expire + end + + describe "when initializing" do + + it "should work without a configuration file" do + FileTest.stubs(:exists?).with(@path).returns(false) + proc { Puppet::FileServing::Configuration.create }.should_not raise_error + end + + it "should parse the configuration file if present" do + FileTest.stubs(:exists?).with(@path).returns(true) + @parser = mock 'parser' + @parser.expects(:parse).returns({}) + Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) + Puppet::FileServing::Configuration.create + end + + it "should determine the path to the configuration file from the Puppet settings" do + Puppet::FileServing::Configuration.create + end + end + + describe "when parsing the configuration file" do + + before do + FileTest.stubs(:exists?).with(@path).returns(true) + @parser = mock 'parser' + Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) + end + + it "should set the mount list to the results of parsing" do + @parser.expects(:parse).returns("one" => mock("mount")) + config = Puppet::FileServing::Configuration.create + config.mounted?("one").should be_true + end + + it "should not raise exceptions" do + @parser.expects(:parse).raises(ArgumentError) + proc { Puppet::FileServing::Configuration.create }.should_not raise_error + end + + it "should replace the existing mount list with the results of reparsing" do + @parser.expects(:parse).returns("one" => mock("mount")) + config = Puppet::FileServing::Configuration.create + config.mounted?("one").should be_true + # Now parse again + @parser.expects(:parse).returns("two" => mock('other')) + config.send(:readconfig, false) + config.mounted?("one").should be_false + config.mounted?("two").should be_true + end + + it "should not replace the mount list until the file is entirely parsed successfully" do + @parser.expects(:parse).returns("one" => mock("mount")) + @parser.expects(:parse).raises(ArgumentError) + config = Puppet::FileServing::Configuration.create + # Now parse again, so the exception gets thrown + config.send(:readconfig, false) + config.mounted?("one").should be_true + end + + it "should add modules and plugins mounts even if the file does not exist" do + FileTest.expects(:exists?).returns false # the file doesn't exist + config = Puppet::FileServing::Configuration.create + config.mounted?("modules").should be_true + config.mounted?("plugins").should be_true + end + + it "should allow all access to modules and plugins if no fileserver.conf exists" do + FileTest.expects(:exists?).returns false # the file doesn't exist + modules = stub 'modules', :empty? => true + Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) + modules.expects(:allow).with('*') + + plugins = stub 'plugins', :empty? => true + Puppet::FileServing::Mount::Plugins.stubs(:new).returns(plugins) + plugins.expects(:allow).with('*') + + Puppet::FileServing::Configuration.create + end + + it "should not allow access from all to modules and plugins if the fileserver.conf provided some rules" do + FileTest.expects(:exists?).returns false # the file doesn't exist + + modules = stub 'modules', :empty? => false + Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) + modules.expects(:allow).with('*').never + + plugins = stub 'plugins', :empty? => false + Puppet::FileServing::Mount::Plugins.stubs(:new).returns(plugins) + plugins.expects(:allow).with('*').never + + Puppet::FileServing::Configuration.create + end + + it "should add modules and plugins mounts even if they are not returned by the parser" do + @parser.expects(:parse).returns("one" => mock("mount")) + FileTest.expects(:exists?).returns true # the file doesn't exist + config = Puppet::FileServing::Configuration.create + config.mounted?("modules").should be_true + config.mounted?("plugins").should be_true + end + end + + describe "when finding the specified mount" do + it "should choose the named mount if one exists" do + config = Puppet::FileServing::Configuration.create + config.expects(:mounts).returns("one" => "foo") + config.find_mount("one", mock('env')).should == "foo" + end + + it "should use the provided environment to find a matching module if the named module cannot be found" do + config = Puppet::FileServing::Configuration.create + + mod = mock 'module' + env = mock 'environment' + env.expects(:module).with("foo").returns mod + mount = mock 'mount' + + config.stubs(:mounts).returns("modules" => mount) + Puppet::Util::Warnings.expects(:notice_once) + config.find_mount("foo", env).should equal(mount) + end + + it "should return nil if there is no such named mount and no module with the same name exists" do + config = Puppet::FileServing::Configuration.create + + env = mock 'environment' + env.expects(:module).with("foo").returns nil + + mount = mock 'mount' + config.stubs(:mounts).returns("modules" => mount) + config.find_mount("foo", env).should be_nil + end + end + + describe "when finding the mount name and relative path in a request key" do + before do + @config = Puppet::FileServing::Configuration.create + @config.stubs(:find_mount) + + @request = stub 'request', :key => "foo/bar/baz", :options => {}, :node => nil, :environment => mock("env") + end + + it "should reread the configuration" do + @config.expects(:readconfig) + + @config.split_path(@request) + end + + it "should treat the first field of the URI path as the mount name" do + @config.expects(:find_mount).with { |name, node| name == "foo" } + + @config.split_path(@request) + end + + it "should fail if the mount name is not alpha-numeric" do + @request.expects(:key).returns "foo&bar/asdf" + + lambda { @config.split_path(@request) }.should raise_error(ArgumentError) + end + + it "should support dashes in the mount name" do + @request.expects(:key).returns "foo-bar/asdf" + + lambda { @config.split_path(@request) }.should_not raise_error(ArgumentError) + end + + it "should use the mount name and environment to find the mount" do + @config.expects(:find_mount).with { |name, env| name == "foo" and env == @request.environment } + @request.stubs(:node).returns("mynode") + + @config.split_path(@request) + end + + it "should return nil if the mount cannot be found" do + @config.expects(:find_mount).returns nil + + @config.split_path(@request).should be_nil + end + + it "should return the mount and the relative path if the mount is found" do + mount = stub 'mount', :name => "foo" + @config.expects(:find_mount).returns mount + + @config.split_path(@request).should == [mount, "bar/baz"] + end + + it "should remove any double slashes" do + @request.stubs(:key).returns "foo/bar//baz" + mount = stub 'mount', :name => "foo" + @config.expects(:find_mount).returns mount + + @config.split_path(@request).should == [mount, "bar/baz"] + end + + it "should return the relative path as nil if it is an empty string" do + @request.expects(:key).returns "foo" + mount = stub 'mount', :name => "foo" + @config.expects(:find_mount).returns mount + + @config.split_path(@request).should == [mount, nil] + end + + it "should add 'modules/' to the relative path if the modules mount is used but not specified, for backward compatibility" do + @request.expects(:key).returns "foo/bar" + mount = stub 'mount', :name => "modules" + @config.expects(:find_mount).returns mount + + @config.split_path(@request).should == [mount, "foo/bar"] + end + end +end diff --git a/spec/unit/file_serving/configuration_spec_spec.rb b/spec/unit/file_serving/configuration_spec_spec.rb deleted file mode 100755 index 4621a0c82..000000000 --- a/spec/unit/file_serving/configuration_spec_spec.rb +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/file_serving/configuration' - -describe Puppet::FileServing::Configuration do - it "should make :new a private method" do - proc { Puppet::FileServing::Configuration.new }.should raise_error - end - - it "should return the same configuration each time :create is called" do - Puppet::FileServing::Configuration.create.should equal(Puppet::FileServing::Configuration.create) - end - - it "should have a method for removing the current configuration instance" do - old = Puppet::FileServing::Configuration.create - Puppet::Util::Cacher.expire - Puppet::FileServing::Configuration.create.should_not equal(old) - end - - after do - Puppet::Util::Cacher.expire - end -end - -describe Puppet::FileServing::Configuration do - - before :each do - @path = "/path/to/configuration/file.conf" - Puppet.settings.stubs(:value).with(:trace).returns(false) - Puppet.settings.stubs(:value).with(:fileserverconfig).returns(@path) - end - - after :each do - Puppet::Util::Cacher.expire - end - - describe "when initializing" do - - it "should work without a configuration file" do - FileTest.stubs(:exists?).with(@path).returns(false) - proc { Puppet::FileServing::Configuration.create }.should_not raise_error - end - - it "should parse the configuration file if present" do - FileTest.stubs(:exists?).with(@path).returns(true) - @parser = mock 'parser' - @parser.expects(:parse).returns({}) - Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) - Puppet::FileServing::Configuration.create - end - - it "should determine the path to the configuration file from the Puppet settings" do - Puppet::FileServing::Configuration.create - end - end - - describe "when parsing the configuration file" do - - before do - FileTest.stubs(:exists?).with(@path).returns(true) - @parser = mock 'parser' - Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) - end - - it "should set the mount list to the results of parsing" do - @parser.expects(:parse).returns("one" => mock("mount")) - config = Puppet::FileServing::Configuration.create - config.mounted?("one").should be_true - end - - it "should not raise exceptions" do - @parser.expects(:parse).raises(ArgumentError) - proc { Puppet::FileServing::Configuration.create }.should_not raise_error - end - - it "should replace the existing mount list with the results of reparsing" do - @parser.expects(:parse).returns("one" => mock("mount")) - config = Puppet::FileServing::Configuration.create - config.mounted?("one").should be_true - # Now parse again - @parser.expects(:parse).returns("two" => mock('other')) - config.send(:readconfig, false) - config.mounted?("one").should be_false - config.mounted?("two").should be_true - end - - it "should not replace the mount list until the file is entirely parsed successfully" do - @parser.expects(:parse).returns("one" => mock("mount")) - @parser.expects(:parse).raises(ArgumentError) - config = Puppet::FileServing::Configuration.create - # Now parse again, so the exception gets thrown - config.send(:readconfig, false) - config.mounted?("one").should be_true - end - - it "should add modules and plugins mounts even if the file does not exist" do - FileTest.expects(:exists?).returns false # the file doesn't exist - config = Puppet::FileServing::Configuration.create - config.mounted?("modules").should be_true - config.mounted?("plugins").should be_true - end - - it "should allow all access to modules and plugins if no fileserver.conf exists" do - FileTest.expects(:exists?).returns false # the file doesn't exist - modules = stub 'modules', :empty? => true - Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) - modules.expects(:allow).with('*') - - plugins = stub 'plugins', :empty? => true - Puppet::FileServing::Mount::Plugins.stubs(:new).returns(plugins) - plugins.expects(:allow).with('*') - - Puppet::FileServing::Configuration.create - end - - it "should not allow access from all to modules and plugins if the fileserver.conf provided some rules" do - FileTest.expects(:exists?).returns false # the file doesn't exist - - modules = stub 'modules', :empty? => false - Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) - modules.expects(:allow).with('*').never - - plugins = stub 'plugins', :empty? => false - Puppet::FileServing::Mount::Plugins.stubs(:new).returns(plugins) - plugins.expects(:allow).with('*').never - - Puppet::FileServing::Configuration.create - end - - it "should add modules and plugins mounts even if they are not returned by the parser" do - @parser.expects(:parse).returns("one" => mock("mount")) - FileTest.expects(:exists?).returns true # the file doesn't exist - config = Puppet::FileServing::Configuration.create - config.mounted?("modules").should be_true - config.mounted?("plugins").should be_true - end - end - - describe "when finding the specified mount" do - it "should choose the named mount if one exists" do - config = Puppet::FileServing::Configuration.create - config.expects(:mounts).returns("one" => "foo") - config.find_mount("one", mock('env')).should == "foo" - end - - it "should use the provided environment to find a matching module if the named module cannot be found" do - config = Puppet::FileServing::Configuration.create - - mod = mock 'module' - env = mock 'environment' - env.expects(:module).with("foo").returns mod - mount = mock 'mount' - - config.stubs(:mounts).returns("modules" => mount) - Puppet::Util::Warnings.expects(:notice_once) - config.find_mount("foo", env).should equal(mount) - end - - it "should return nil if there is no such named mount and no module with the same name exists" do - config = Puppet::FileServing::Configuration.create - - env = mock 'environment' - env.expects(:module).with("foo").returns nil - - mount = mock 'mount' - config.stubs(:mounts).returns("modules" => mount) - config.find_mount("foo", env).should be_nil - end - end - - describe "when finding the mount name and relative path in a request key" do - before do - @config = Puppet::FileServing::Configuration.create - @config.stubs(:find_mount) - - @request = stub 'request', :key => "foo/bar/baz", :options => {}, :node => nil, :environment => mock("env") - end - - it "should reread the configuration" do - @config.expects(:readconfig) - - @config.split_path(@request) - end - - it "should treat the first field of the URI path as the mount name" do - @config.expects(:find_mount).with { |name, node| name == "foo" } - - @config.split_path(@request) - end - - it "should fail if the mount name is not alpha-numeric" do - @request.expects(:key).returns "foo&bar/asdf" - - lambda { @config.split_path(@request) }.should raise_error(ArgumentError) - end - - it "should support dashes in the mount name" do - @request.expects(:key).returns "foo-bar/asdf" - - lambda { @config.split_path(@request) }.should_not raise_error(ArgumentError) - end - - it "should use the mount name and environment to find the mount" do - @config.expects(:find_mount).with { |name, env| name == "foo" and env == @request.environment } - @request.stubs(:node).returns("mynode") - - @config.split_path(@request) - end - - it "should return nil if the mount cannot be found" do - @config.expects(:find_mount).returns nil - - @config.split_path(@request).should be_nil - end - - it "should return the mount and the relative path if the mount is found" do - mount = stub 'mount', :name => "foo" - @config.expects(:find_mount).returns mount - - @config.split_path(@request).should == [mount, "bar/baz"] - end - - it "should remove any double slashes" do - @request.stubs(:key).returns "foo/bar//baz" - mount = stub 'mount', :name => "foo" - @config.expects(:find_mount).returns mount - - @config.split_path(@request).should == [mount, "bar/baz"] - end - - it "should return the relative path as nil if it is an empty string" do - @request.expects(:key).returns "foo" - mount = stub 'mount', :name => "foo" - @config.expects(:find_mount).returns mount - - @config.split_path(@request).should == [mount, nil] - end - - it "should add 'modules/' to the relative path if the modules mount is used but not specified, for backward compatibility" do - @request.expects(:key).returns "foo/bar" - mount = stub 'mount', :name => "modules" - @config.expects(:find_mount).returns mount - - @config.split_path(@request).should == [mount, "foo/bar"] - end - end -end diff --git a/spec/unit/file_serving/content_spec.rb b/spec/unit/file_serving/content_spec.rb new file mode 100755 index 000000000..f772b861f --- /dev/null +++ b/spec/unit/file_serving/content_spec.rb @@ -0,0 +1,118 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/file_serving/content' + +describe Puppet::FileServing::Content do + it "should should be a subclass of Base" do + Puppet::FileServing::Content.superclass.should equal(Puppet::FileServing::Base) + end + + it "should indirect file_content" do + Puppet::FileServing::Content.indirection.name.should == :file_content + end + + it "should should include the IndirectionHooks module in its indirection" do + Puppet::FileServing::Content.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks) + end + + it "should only support the raw format" do + Puppet::FileServing::Content.supported_formats.should == [:raw] + end + + it "should have a method for collecting its attributes" do + Puppet::FileServing::Content.new("/path").should respond_to(:collect) + end + + it "should not retrieve and store its contents when its attributes are collected if the file is a normal file" do + content = Puppet::FileServing::Content.new("/path") + + result = "foo" + File.stubs(:lstat).returns(stub("stat", :ftype => "file")) + File.expects(:read).with("/path").never + content.collect + + content.instance_variable_get("@content").should be_nil + end + + it "should not attempt to retrieve its contents if the file is a directory" do + content = Puppet::FileServing::Content.new("/path") + + result = "foo" + File.stubs(:lstat).returns(stub("stat", :ftype => "directory")) + File.expects(:read).with("/path").never + content.collect + + content.instance_variable_get("@content").should be_nil + end + + it "should have a method for setting its content" do + content = Puppet::FileServing::Content.new("/path") + content.should respond_to(:content=) + end + + it "should make content available when set externally" do + content = Puppet::FileServing::Content.new("/path") + content.content = "foo/bar" + content.content.should == "foo/bar" + end + + it "should be able to create a content instance from raw file contents" do + Puppet::FileServing::Content.should respond_to(:from_raw) + end + + it "should create an instance with a fake file name and correct content when converting from raw" do + instance = mock 'instance' + Puppet::FileServing::Content.expects(:new).with("/this/is/a/fake/path").returns instance + + instance.expects(:content=).with "foo/bar" + + Puppet::FileServing::Content.from_raw("foo/bar").should equal(instance) + end + + it "should return an opened File when converted to raw" do + content = Puppet::FileServing::Content.new("/path") + + File.expects(:new).with("/path","r").returns :file + + content.to_raw.should == :file + end +end + +describe Puppet::FileServing::Content, "when returning the contents" do + before do + @path = "/my/path" + @content = Puppet::FileServing::Content.new(@path, :links => :follow) + end + + it "should fail if the file is a symlink and links are set to :manage" do + @content.links = :manage + File.expects(:lstat).with(@path).returns stub("stat", :ftype => "symlink") + proc { @content.content }.should raise_error(ArgumentError) + end + + it "should fail if a path is not set" do + proc { @content.content() }.should raise_error(Errno::ENOENT) + end + + it "should raise Errno::ENOENT if the file is absent" do + @content.path = "/there/is/absolutely/no/chance/that/this/path/exists" + proc { @content.content() }.should raise_error(Errno::ENOENT) + end + + it "should return the contents of the path if the file exists" do + File.expects(:stat).with(@path).returns stub("stat", :ftype => "file") + File.expects(:read).with(@path).returns(:mycontent) + @content.content.should == :mycontent + end + + it "should cache the returned contents" do + File.expects(:stat).with(@path).returns stub("stat", :ftype => "file") + File.expects(:read).with(@path).returns(:mycontent) + @content.content + + # The second run would throw a failure if the content weren't being cached. + @content.content + end +end diff --git a/spec/unit/file_serving/content_spec_spec.rb b/spec/unit/file_serving/content_spec_spec.rb deleted file mode 100755 index f772b861f..000000000 --- a/spec/unit/file_serving/content_spec_spec.rb +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/file_serving/content' - -describe Puppet::FileServing::Content do - it "should should be a subclass of Base" do - Puppet::FileServing::Content.superclass.should equal(Puppet::FileServing::Base) - end - - it "should indirect file_content" do - Puppet::FileServing::Content.indirection.name.should == :file_content - end - - it "should should include the IndirectionHooks module in its indirection" do - Puppet::FileServing::Content.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks) - end - - it "should only support the raw format" do - Puppet::FileServing::Content.supported_formats.should == [:raw] - end - - it "should have a method for collecting its attributes" do - Puppet::FileServing::Content.new("/path").should respond_to(:collect) - end - - it "should not retrieve and store its contents when its attributes are collected if the file is a normal file" do - content = Puppet::FileServing::Content.new("/path") - - result = "foo" - File.stubs(:lstat).returns(stub("stat", :ftype => "file")) - File.expects(:read).with("/path").never - content.collect - - content.instance_variable_get("@content").should be_nil - end - - it "should not attempt to retrieve its contents if the file is a directory" do - content = Puppet::FileServing::Content.new("/path") - - result = "foo" - File.stubs(:lstat).returns(stub("stat", :ftype => "directory")) - File.expects(:read).with("/path").never - content.collect - - content.instance_variable_get("@content").should be_nil - end - - it "should have a method for setting its content" do - content = Puppet::FileServing::Content.new("/path") - content.should respond_to(:content=) - end - - it "should make content available when set externally" do - content = Puppet::FileServing::Content.new("/path") - content.content = "foo/bar" - content.content.should == "foo/bar" - end - - it "should be able to create a content instance from raw file contents" do - Puppet::FileServing::Content.should respond_to(:from_raw) - end - - it "should create an instance with a fake file name and correct content when converting from raw" do - instance = mock 'instance' - Puppet::FileServing::Content.expects(:new).with("/this/is/a/fake/path").returns instance - - instance.expects(:content=).with "foo/bar" - - Puppet::FileServing::Content.from_raw("foo/bar").should equal(instance) - end - - it "should return an opened File when converted to raw" do - content = Puppet::FileServing::Content.new("/path") - - File.expects(:new).with("/path","r").returns :file - - content.to_raw.should == :file - end -end - -describe Puppet::FileServing::Content, "when returning the contents" do - before do - @path = "/my/path" - @content = Puppet::FileServing::Content.new(@path, :links => :follow) - end - - it "should fail if the file is a symlink and links are set to :manage" do - @content.links = :manage - File.expects(:lstat).with(@path).returns stub("stat", :ftype => "symlink") - proc { @content.content }.should raise_error(ArgumentError) - end - - it "should fail if a path is not set" do - proc { @content.content() }.should raise_error(Errno::ENOENT) - end - - it "should raise Errno::ENOENT if the file is absent" do - @content.path = "/there/is/absolutely/no/chance/that/this/path/exists" - proc { @content.content() }.should raise_error(Errno::ENOENT) - end - - it "should return the contents of the path if the file exists" do - File.expects(:stat).with(@path).returns stub("stat", :ftype => "file") - File.expects(:read).with(@path).returns(:mycontent) - @content.content.should == :mycontent - end - - it "should cache the returned contents" do - File.expects(:stat).with(@path).returns stub("stat", :ftype => "file") - File.expects(:read).with(@path).returns(:mycontent) - @content.content - - # The second run would throw a failure if the content weren't being cached. - @content.content - end -end diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb new file mode 100755 index 000000000..c03522d0e --- /dev/null +++ b/spec/unit/file_serving/fileset_spec.rb @@ -0,0 +1,347 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/file_serving/fileset' + +describe Puppet::FileServing::Fileset, " when initializing" do + it "should require a path" do + proc { Puppet::FileServing::Fileset.new }.should raise_error(ArgumentError) + end + + it "should fail if its path is not fully qualified" do + proc { Puppet::FileServing::Fileset.new("some/file") }.should raise_error(ArgumentError) + end + + it "should fail if its path does not exist" do + File.expects(:lstat).with("/some/file").returns nil + proc { Puppet::FileServing::Fileset.new("/some/file") }.should raise_error(ArgumentError) + end + + it "should accept a 'recurse' option" do + File.expects(:lstat).with("/some/file").returns stub("stat") + set = Puppet::FileServing::Fileset.new("/some/file", :recurse => true) + set.recurse.should be_true + end + + it "should accept a 'recurselimit' option" do + File.expects(:lstat).with("/some/file").returns stub("stat") + set = Puppet::FileServing::Fileset.new("/some/file", :recurselimit => 3) + set.recurselimit.should == 3 + end + + it "should accept an 'ignore' option" do + File.expects(:lstat).with("/some/file").returns stub("stat") + set = Puppet::FileServing::Fileset.new("/some/file", :ignore => ".svn") + set.ignore.should == [".svn"] + end + + it "should accept a 'links' option" do + File.expects(:lstat).with("/some/file").returns stub("stat") + set = Puppet::FileServing::Fileset.new("/some/file", :links => :manage) + set.links.should == :manage + end + + it "should accept a 'checksum_type' option" do + File.expects(:lstat).with("/some/file").returns stub("stat") + set = Puppet::FileServing::Fileset.new("/some/file", :checksum_type => :test) + set.checksum_type.should == :test + end + + it "should fail if 'links' is set to anything other than :manage or :follow" do + proc { Puppet::FileServing::Fileset.new("/some/file", :links => :whatever) }.should raise_error(ArgumentError) + end + + it "should default to 'false' for recurse" do + File.expects(:lstat).with("/some/file").returns stub("stat") + Puppet::FileServing::Fileset.new("/some/file").recurse.should == false + end + + it "should default to :infinite for recurselimit" do + File.expects(:lstat).with("/some/file").returns stub("stat") + Puppet::FileServing::Fileset.new("/some/file").recurselimit.should == :infinite + end + + it "should default to an empty ignore list" do + File.expects(:lstat).with("/some/file").returns stub("stat") + Puppet::FileServing::Fileset.new("/some/file").ignore.should == [] + end + + it "should default to :manage for links" do + File.expects(:lstat).with("/some/file").returns stub("stat") + Puppet::FileServing::Fileset.new("/some/file").links.should == :manage + end + + it "should support using an Indirector Request for its options" do + File.expects(:lstat).with("/some/file").returns stub("stat") + request = Puppet::Indirector::Request.new(:file_serving, :find, "foo") + lambda { Puppet::FileServing::Fileset.new("/some/file", request) }.should_not raise_error + end + + describe "using an indirector request" do + before do + File.stubs(:lstat).returns stub("stat") + @values = {:links => :manage, :ignore => %w{a b}, :recurse => true, :recurselimit => 1234} + @request = Puppet::Indirector::Request.new(:file_serving, :find, "foo") + end + + [:recurse, :recurselimit, :ignore, :links].each do |option| + it "should pass :recurse, :recurselimit, :ignore, and :links settings on to the fileset if present" do + @request.stubs(:options).returns(option => @values[option]) + Puppet::FileServing::Fileset.new("/my/file", @request).send(option).should == @values[option] + end + + it "should pass :recurse, :recurselimit, :ignore, and :links settings on to the fileset if present with the keys stored as strings" do + @request.stubs(:options).returns(option.to_s => @values[option]) + Puppet::FileServing::Fileset.new("/my/file", @request).send(option).should == @values[option] + end + end + + it "should convert the integer as a string to their integer counterpart when setting options" do + @request.stubs(:options).returns(:recurselimit => "1234") + Puppet::FileServing::Fileset.new("/my/file", @request).recurselimit.should == 1234 + end + + it "should convert the string 'true' to the boolean true when setting options" do + @request.stubs(:options).returns(:recurse => "true") + Puppet::FileServing::Fileset.new("/my/file", @request).recurse.should == true + end + + it "should convert the string 'false' to the boolean false when setting options" do + @request.stubs(:options).returns(:recurse => "false") + Puppet::FileServing::Fileset.new("/my/file", @request).recurse.should == false + end + end +end + +describe Puppet::FileServing::Fileset, " when determining whether to recurse" do + before do + @path = "/my/path" + File.expects(:lstat).with(@path).returns stub("stat") + @fileset = Puppet::FileServing::Fileset.new(@path) + end + + it "should always recurse if :recurse is set to 'true' and with infinite recursion" do + @fileset.recurse = true + @fileset.recurselimit = :infinite + @fileset.recurse?(0).should be_true + end + + it "should never recurse if :recurse is set to 'false'" do + @fileset.recurse = false + @fileset.recurse?(-1).should be_false + end + + it "should recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is less than that integer" do + @fileset.recurse = true + @fileset.recurselimit = 1 + @fileset.recurse?(0).should be_true + end + + it "should recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is equal to that integer" do + @fileset.recurse = true + @fileset.recurselimit = 1 + @fileset.recurse?(1).should be_true + end + + it "should not recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is greater than that integer" do + @fileset.recurse = true + @fileset.recurselimit = 1 + @fileset.recurse?(2).should be_false + end +end + +describe Puppet::FileServing::Fileset, " when recursing" do + before do + @path = "/my/path" + File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) + @fileset = Puppet::FileServing::Fileset.new(@path) + + @dirstat = stub 'dirstat', :directory? => true + @filestat = stub 'filestat', :directory? => false + end + + def mock_dir_structure(path, stat_method = :lstat) + File.stubs(stat_method).with(path).returns(@dirstat) + Dir.stubs(:entries).with(path).returns(%w{one two .svn CVS}) + + # Keep track of the files we're stubbing. + @files = %w{.} + + %w{one two .svn CVS}.each do |subdir| + @files << subdir # relative path + subpath = File.join(path, subdir) + File.stubs(stat_method).with(subpath).returns(@dirstat) + Dir.stubs(:entries).with(subpath).returns(%w{.svn CVS file1 file2}) + %w{file1 file2 .svn CVS}.each do |file| + @files << File.join(subdir, file) # relative path + File.stubs(stat_method).with(File.join(subpath, file)).returns(@filestat) + end + end + end + + it "should recurse through the whole file tree if :recurse is set to 'true'" do + mock_dir_structure(@path) + @fileset.stubs(:recurse?).returns(true) + @fileset.files.sort.should == @files.sort + end + + it "should not recurse if :recurse is set to 'false'" do + mock_dir_structure(@path) + @fileset.stubs(:recurse?).returns(false) + @fileset.files.should == %w{.} + end + + # It seems like I should stub :recurse? here, or that I shouldn't stub the + # examples above, but... + it "should recurse to the level set if :recurselimit is set to an integer" do + mock_dir_structure(@path) + @fileset.recurse = true + @fileset.recurselimit = 1 + @fileset.files.should == %w{. one two .svn CVS} + end + + it "should ignore the '.' and '..' directories in subdirectories" do + mock_dir_structure(@path) + @fileset.recurse = true + @fileset.files.sort.should == @files.sort + end + + it "should function if the :ignore value provided is nil" do + mock_dir_structure(@path) + @fileset.recurse = true + @fileset.ignore = nil + lambda { @fileset.files }.should_not raise_error + end + + it "should ignore files that match a single pattern in the ignore list" do + mock_dir_structure(@path) + @fileset.recurse = true + @fileset.ignore = ".svn" + @fileset.files.find { |file| file.include?(".svn") }.should be_nil + end + + it "should ignore files that match any of multiple patterns in the ignore list" do + mock_dir_structure(@path) + @fileset.recurse = true + @fileset.ignore = %w{.svn CVS} + @fileset.files.find { |file| file.include?(".svn") or file.include?("CVS") }.should be_nil + end + + it "should use File.stat if :links is set to :follow" do + mock_dir_structure(@path, :stat) + @fileset.recurse = true + @fileset.links = :follow + @fileset.files.sort.should == @files.sort + end + + it "should use File.lstat if :links is set to :manage" do + mock_dir_structure(@path, :lstat) + @fileset.recurse = true + @fileset.links = :manage + @fileset.files.sort.should == @files.sort + end + + it "should succeed when paths have regexp significant characters" do + @path = "/my/path/rV1x2DafFr0R6tGG+1bbk++++TM" + File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) + @fileset = Puppet::FileServing::Fileset.new(@path) + mock_dir_structure(@path) + @fileset.recurse = true + @fileset.files.sort.should == @files.sort + end +end + +describe Puppet::FileServing::Fileset, " when following links that point to missing files" do + before do + @path = "/my/path" + File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) + @fileset = Puppet::FileServing::Fileset.new(@path) + @fileset.links = :follow + @fileset.recurse = true + + @stat = stub 'stat', :directory? => true + + File.expects(:stat).with(@path).returns(@stat) + File.expects(:stat).with(File.join(@path, "mylink")).raises(Errno::ENOENT) + Dir.stubs(:entries).with(@path).returns(["mylink"]) + end + + it "should not fail" do + proc { @fileset.files }.should_not raise_error + end + + it "should still manage the link" do + @fileset.files.sort.should == %w{. mylink}.sort + end +end + +describe Puppet::FileServing::Fileset, " when ignoring" do + before do + @path = "/my/path" + File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) + @fileset = Puppet::FileServing::Fileset.new(@path) + end + + it "should use ruby's globbing to determine what files should be ignored" do + @fileset.ignore = ".svn" + File.expects(:fnmatch?).with(".svn", "my_file") + @fileset.ignore?("my_file") + end + + it "should ignore files whose paths match a single provided ignore value" do + @fileset.ignore = ".svn" + File.stubs(:fnmatch?).with(".svn", "my_file").returns true + @fileset.ignore?("my_file").should be_true + end + + it "should ignore files whose paths match any of multiple provided ignore values" do + @fileset.ignore = [".svn", "CVS"] + File.stubs(:fnmatch?).with(".svn", "my_file").returns false + File.stubs(:fnmatch?).with("CVS", "my_file").returns true + @fileset.ignore?("my_file").should be_true + end +end + +describe Puppet::FileServing::Fileset, "when merging other filesets" do + before do + @paths = %w{/first/path /second/path /third/path} + + @filesets = @paths.collect do |path| + File.stubs(:lstat).with(path).returns stub("stat", :directory? => true) + Puppet::FileServing::Fileset.new(path, :recurse => true) + end + + Dir.stubs(:entries).returns [] + end + + it "should return a hash of all files in each fileset with the value being the base path" do + Dir.expects(:entries).with("/first/path").returns(%w{one uno}) + Dir.expects(:entries).with("/second/path").returns(%w{two dos}) + Dir.expects(:entries).with("/third/path").returns(%w{three tres}) + + Puppet::FileServing::Fileset.merge(*@filesets).should == { + "." => "/first/path", + "one" => "/first/path", + "uno" => "/first/path", + "two" => "/second/path", + "dos" => "/second/path", + "three" => "/third/path", + "tres" => "/third/path", + } + end + + it "should include the base directory from the first fileset" do + Dir.expects(:entries).with("/first/path").returns(%w{one}) + Dir.expects(:entries).with("/second/path").returns(%w{two}) + + Puppet::FileServing::Fileset.merge(*@filesets)["."].should == "/first/path" + end + + it "should use the base path of the first found file when relative file paths conflict" do + Dir.expects(:entries).with("/first/path").returns(%w{one}) + Dir.expects(:entries).with("/second/path").returns(%w{one}) + + Puppet::FileServing::Fileset.merge(*@filesets)["one"].should == "/first/path" + end +end diff --git a/spec/unit/file_serving/fileset_spec_spec.rb b/spec/unit/file_serving/fileset_spec_spec.rb deleted file mode 100755 index c03522d0e..000000000 --- a/spec/unit/file_serving/fileset_spec_spec.rb +++ /dev/null @@ -1,347 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/file_serving/fileset' - -describe Puppet::FileServing::Fileset, " when initializing" do - it "should require a path" do - proc { Puppet::FileServing::Fileset.new }.should raise_error(ArgumentError) - end - - it "should fail if its path is not fully qualified" do - proc { Puppet::FileServing::Fileset.new("some/file") }.should raise_error(ArgumentError) - end - - it "should fail if its path does not exist" do - File.expects(:lstat).with("/some/file").returns nil - proc { Puppet::FileServing::Fileset.new("/some/file") }.should raise_error(ArgumentError) - end - - it "should accept a 'recurse' option" do - File.expects(:lstat).with("/some/file").returns stub("stat") - set = Puppet::FileServing::Fileset.new("/some/file", :recurse => true) - set.recurse.should be_true - end - - it "should accept a 'recurselimit' option" do - File.expects(:lstat).with("/some/file").returns stub("stat") - set = Puppet::FileServing::Fileset.new("/some/file", :recurselimit => 3) - set.recurselimit.should == 3 - end - - it "should accept an 'ignore' option" do - File.expects(:lstat).with("/some/file").returns stub("stat") - set = Puppet::FileServing::Fileset.new("/some/file", :ignore => ".svn") - set.ignore.should == [".svn"] - end - - it "should accept a 'links' option" do - File.expects(:lstat).with("/some/file").returns stub("stat") - set = Puppet::FileServing::Fileset.new("/some/file", :links => :manage) - set.links.should == :manage - end - - it "should accept a 'checksum_type' option" do - File.expects(:lstat).with("/some/file").returns stub("stat") - set = Puppet::FileServing::Fileset.new("/some/file", :checksum_type => :test) - set.checksum_type.should == :test - end - - it "should fail if 'links' is set to anything other than :manage or :follow" do - proc { Puppet::FileServing::Fileset.new("/some/file", :links => :whatever) }.should raise_error(ArgumentError) - end - - it "should default to 'false' for recurse" do - File.expects(:lstat).with("/some/file").returns stub("stat") - Puppet::FileServing::Fileset.new("/some/file").recurse.should == false - end - - it "should default to :infinite for recurselimit" do - File.expects(:lstat).with("/some/file").returns stub("stat") - Puppet::FileServing::Fileset.new("/some/file").recurselimit.should == :infinite - end - - it "should default to an empty ignore list" do - File.expects(:lstat).with("/some/file").returns stub("stat") - Puppet::FileServing::Fileset.new("/some/file").ignore.should == [] - end - - it "should default to :manage for links" do - File.expects(:lstat).with("/some/file").returns stub("stat") - Puppet::FileServing::Fileset.new("/some/file").links.should == :manage - end - - it "should support using an Indirector Request for its options" do - File.expects(:lstat).with("/some/file").returns stub("stat") - request = Puppet::Indirector::Request.new(:file_serving, :find, "foo") - lambda { Puppet::FileServing::Fileset.new("/some/file", request) }.should_not raise_error - end - - describe "using an indirector request" do - before do - File.stubs(:lstat).returns stub("stat") - @values = {:links => :manage, :ignore => %w{a b}, :recurse => true, :recurselimit => 1234} - @request = Puppet::Indirector::Request.new(:file_serving, :find, "foo") - end - - [:recurse, :recurselimit, :ignore, :links].each do |option| - it "should pass :recurse, :recurselimit, :ignore, and :links settings on to the fileset if present" do - @request.stubs(:options).returns(option => @values[option]) - Puppet::FileServing::Fileset.new("/my/file", @request).send(option).should == @values[option] - end - - it "should pass :recurse, :recurselimit, :ignore, and :links settings on to the fileset if present with the keys stored as strings" do - @request.stubs(:options).returns(option.to_s => @values[option]) - Puppet::FileServing::Fileset.new("/my/file", @request).send(option).should == @values[option] - end - end - - it "should convert the integer as a string to their integer counterpart when setting options" do - @request.stubs(:options).returns(:recurselimit => "1234") - Puppet::FileServing::Fileset.new("/my/file", @request).recurselimit.should == 1234 - end - - it "should convert the string 'true' to the boolean true when setting options" do - @request.stubs(:options).returns(:recurse => "true") - Puppet::FileServing::Fileset.new("/my/file", @request).recurse.should == true - end - - it "should convert the string 'false' to the boolean false when setting options" do - @request.stubs(:options).returns(:recurse => "false") - Puppet::FileServing::Fileset.new("/my/file", @request).recurse.should == false - end - end -end - -describe Puppet::FileServing::Fileset, " when determining whether to recurse" do - before do - @path = "/my/path" - File.expects(:lstat).with(@path).returns stub("stat") - @fileset = Puppet::FileServing::Fileset.new(@path) - end - - it "should always recurse if :recurse is set to 'true' and with infinite recursion" do - @fileset.recurse = true - @fileset.recurselimit = :infinite - @fileset.recurse?(0).should be_true - end - - it "should never recurse if :recurse is set to 'false'" do - @fileset.recurse = false - @fileset.recurse?(-1).should be_false - end - - it "should recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is less than that integer" do - @fileset.recurse = true - @fileset.recurselimit = 1 - @fileset.recurse?(0).should be_true - end - - it "should recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is equal to that integer" do - @fileset.recurse = true - @fileset.recurselimit = 1 - @fileset.recurse?(1).should be_true - end - - it "should not recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is greater than that integer" do - @fileset.recurse = true - @fileset.recurselimit = 1 - @fileset.recurse?(2).should be_false - end -end - -describe Puppet::FileServing::Fileset, " when recursing" do - before do - @path = "/my/path" - File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) - @fileset = Puppet::FileServing::Fileset.new(@path) - - @dirstat = stub 'dirstat', :directory? => true - @filestat = stub 'filestat', :directory? => false - end - - def mock_dir_structure(path, stat_method = :lstat) - File.stubs(stat_method).with(path).returns(@dirstat) - Dir.stubs(:entries).with(path).returns(%w{one two .svn CVS}) - - # Keep track of the files we're stubbing. - @files = %w{.} - - %w{one two .svn CVS}.each do |subdir| - @files << subdir # relative path - subpath = File.join(path, subdir) - File.stubs(stat_method).with(subpath).returns(@dirstat) - Dir.stubs(:entries).with(subpath).returns(%w{.svn CVS file1 file2}) - %w{file1 file2 .svn CVS}.each do |file| - @files << File.join(subdir, file) # relative path - File.stubs(stat_method).with(File.join(subpath, file)).returns(@filestat) - end - end - end - - it "should recurse through the whole file tree if :recurse is set to 'true'" do - mock_dir_structure(@path) - @fileset.stubs(:recurse?).returns(true) - @fileset.files.sort.should == @files.sort - end - - it "should not recurse if :recurse is set to 'false'" do - mock_dir_structure(@path) - @fileset.stubs(:recurse?).returns(false) - @fileset.files.should == %w{.} - end - - # It seems like I should stub :recurse? here, or that I shouldn't stub the - # examples above, but... - it "should recurse to the level set if :recurselimit is set to an integer" do - mock_dir_structure(@path) - @fileset.recurse = true - @fileset.recurselimit = 1 - @fileset.files.should == %w{. one two .svn CVS} - end - - it "should ignore the '.' and '..' directories in subdirectories" do - mock_dir_structure(@path) - @fileset.recurse = true - @fileset.files.sort.should == @files.sort - end - - it "should function if the :ignore value provided is nil" do - mock_dir_structure(@path) - @fileset.recurse = true - @fileset.ignore = nil - lambda { @fileset.files }.should_not raise_error - end - - it "should ignore files that match a single pattern in the ignore list" do - mock_dir_structure(@path) - @fileset.recurse = true - @fileset.ignore = ".svn" - @fileset.files.find { |file| file.include?(".svn") }.should be_nil - end - - it "should ignore files that match any of multiple patterns in the ignore list" do - mock_dir_structure(@path) - @fileset.recurse = true - @fileset.ignore = %w{.svn CVS} - @fileset.files.find { |file| file.include?(".svn") or file.include?("CVS") }.should be_nil - end - - it "should use File.stat if :links is set to :follow" do - mock_dir_structure(@path, :stat) - @fileset.recurse = true - @fileset.links = :follow - @fileset.files.sort.should == @files.sort - end - - it "should use File.lstat if :links is set to :manage" do - mock_dir_structure(@path, :lstat) - @fileset.recurse = true - @fileset.links = :manage - @fileset.files.sort.should == @files.sort - end - - it "should succeed when paths have regexp significant characters" do - @path = "/my/path/rV1x2DafFr0R6tGG+1bbk++++TM" - File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) - @fileset = Puppet::FileServing::Fileset.new(@path) - mock_dir_structure(@path) - @fileset.recurse = true - @fileset.files.sort.should == @files.sort - end -end - -describe Puppet::FileServing::Fileset, " when following links that point to missing files" do - before do - @path = "/my/path" - File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) - @fileset = Puppet::FileServing::Fileset.new(@path) - @fileset.links = :follow - @fileset.recurse = true - - @stat = stub 'stat', :directory? => true - - File.expects(:stat).with(@path).returns(@stat) - File.expects(:stat).with(File.join(@path, "mylink")).raises(Errno::ENOENT) - Dir.stubs(:entries).with(@path).returns(["mylink"]) - end - - it "should not fail" do - proc { @fileset.files }.should_not raise_error - end - - it "should still manage the link" do - @fileset.files.sort.should == %w{. mylink}.sort - end -end - -describe Puppet::FileServing::Fileset, " when ignoring" do - before do - @path = "/my/path" - File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) - @fileset = Puppet::FileServing::Fileset.new(@path) - end - - it "should use ruby's globbing to determine what files should be ignored" do - @fileset.ignore = ".svn" - File.expects(:fnmatch?).with(".svn", "my_file") - @fileset.ignore?("my_file") - end - - it "should ignore files whose paths match a single provided ignore value" do - @fileset.ignore = ".svn" - File.stubs(:fnmatch?).with(".svn", "my_file").returns true - @fileset.ignore?("my_file").should be_true - end - - it "should ignore files whose paths match any of multiple provided ignore values" do - @fileset.ignore = [".svn", "CVS"] - File.stubs(:fnmatch?).with(".svn", "my_file").returns false - File.stubs(:fnmatch?).with("CVS", "my_file").returns true - @fileset.ignore?("my_file").should be_true - end -end - -describe Puppet::FileServing::Fileset, "when merging other filesets" do - before do - @paths = %w{/first/path /second/path /third/path} - - @filesets = @paths.collect do |path| - File.stubs(:lstat).with(path).returns stub("stat", :directory? => true) - Puppet::FileServing::Fileset.new(path, :recurse => true) - end - - Dir.stubs(:entries).returns [] - end - - it "should return a hash of all files in each fileset with the value being the base path" do - Dir.expects(:entries).with("/first/path").returns(%w{one uno}) - Dir.expects(:entries).with("/second/path").returns(%w{two dos}) - Dir.expects(:entries).with("/third/path").returns(%w{three tres}) - - Puppet::FileServing::Fileset.merge(*@filesets).should == { - "." => "/first/path", - "one" => "/first/path", - "uno" => "/first/path", - "two" => "/second/path", - "dos" => "/second/path", - "three" => "/third/path", - "tres" => "/third/path", - } - end - - it "should include the base directory from the first fileset" do - Dir.expects(:entries).with("/first/path").returns(%w{one}) - Dir.expects(:entries).with("/second/path").returns(%w{two}) - - Puppet::FileServing::Fileset.merge(*@filesets)["."].should == "/first/path" - end - - it "should use the base path of the first found file when relative file paths conflict" do - Dir.expects(:entries).with("/first/path").returns(%w{one}) - Dir.expects(:entries).with("/second/path").returns(%w{one}) - - Puppet::FileServing::Fileset.merge(*@filesets)["one"].should == "/first/path" - end -end diff --git a/spec/unit/file_serving/indirection_hooks_spec.rb b/spec/unit/file_serving/indirection_hooks_spec.rb new file mode 100755 index 000000000..fee504bb6 --- /dev/null +++ b/spec/unit/file_serving/indirection_hooks_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/file_serving/indirection_hooks' + +describe Puppet::FileServing::IndirectionHooks do + before do + @object = Object.new + @object.extend(Puppet::FileServing::IndirectionHooks) + + @request = stub 'request', :key => "mymod/myfile", :options => {:node => "whatever"}, :server => nil, :protocol => nil + end + + describe "when being used to select termini" do + it "should return :file if the request key is fully qualified" do + @request.expects(:key).returns "#{File::SEPARATOR}foo" + @object.select_terminus(@request).should == :file + end + + it "should return :file if the URI protocol is set to 'file'" do + @request.expects(:protocol).returns "file" + @object.select_terminus(@request).should == :file + end + + it "should fail when a protocol other than :puppet or :file is used" do + @request.stubs(:protocol).returns "http" + proc { @object.select_terminus(@request) }.should raise_error(ArgumentError) + end + + describe "and the protocol is 'puppet'" do + before do + @request.stubs(:protocol).returns "puppet" + end + + it "should choose :rest when a server is specified" do + @request.stubs(:protocol).returns "puppet" + @request.expects(:server).returns "foo" + @object.select_terminus(@request).should == :rest + end + + # This is so a given file location works when bootstrapping with no server. + it "should choose :rest when the Settings name isn't 'puppet'" do + @request.stubs(:protocol).returns "puppet" + @request.stubs(:server).returns "foo" + Puppet.settings.stubs(:value).with(:name).returns "foo" + @object.select_terminus(@request).should == :rest + end + + it "should choose :file_server when the settings name is 'puppet' and no server is specified" do + modules = mock 'modules' + + @request.expects(:protocol).returns "puppet" + @request.expects(:server).returns nil + Puppet.settings.expects(:value).with(:name).returns "puppet" + @object.select_terminus(@request).should == :file_server + end + end + end +end diff --git a/spec/unit/file_serving/indirection_hooks_spec_spec.rb b/spec/unit/file_serving/indirection_hooks_spec_spec.rb deleted file mode 100755 index fee504bb6..000000000 --- a/spec/unit/file_serving/indirection_hooks_spec_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-10-18. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/file_serving/indirection_hooks' - -describe Puppet::FileServing::IndirectionHooks do - before do - @object = Object.new - @object.extend(Puppet::FileServing::IndirectionHooks) - - @request = stub 'request', :key => "mymod/myfile", :options => {:node => "whatever"}, :server => nil, :protocol => nil - end - - describe "when being used to select termini" do - it "should return :file if the request key is fully qualified" do - @request.expects(:key).returns "#{File::SEPARATOR}foo" - @object.select_terminus(@request).should == :file - end - - it "should return :file if the URI protocol is set to 'file'" do - @request.expects(:protocol).returns "file" - @object.select_terminus(@request).should == :file - end - - it "should fail when a protocol other than :puppet or :file is used" do - @request.stubs(:protocol).returns "http" - proc { @object.select_terminus(@request) }.should raise_error(ArgumentError) - end - - describe "and the protocol is 'puppet'" do - before do - @request.stubs(:protocol).returns "puppet" - end - - it "should choose :rest when a server is specified" do - @request.stubs(:protocol).returns "puppet" - @request.expects(:server).returns "foo" - @object.select_terminus(@request).should == :rest - end - - # This is so a given file location works when bootstrapping with no server. - it "should choose :rest when the Settings name isn't 'puppet'" do - @request.stubs(:protocol).returns "puppet" - @request.stubs(:server).returns "foo" - Puppet.settings.stubs(:value).with(:name).returns "foo" - @object.select_terminus(@request).should == :rest - end - - it "should choose :file_server when the settings name is 'puppet' and no server is specified" do - modules = mock 'modules' - - @request.expects(:protocol).returns "puppet" - @request.expects(:server).returns nil - Puppet.settings.expects(:value).with(:name).returns "puppet" - @object.select_terminus(@request).should == :file_server - end - end - end -end diff --git a/spec/unit/file_serving/metadata_spec.rb b/spec/unit/file_serving/metadata_spec.rb new file mode 100755 index 000000000..ef2b3b6f0 --- /dev/null +++ b/spec/unit/file_serving/metadata_spec.rb @@ -0,0 +1,286 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/file_serving/metadata' + +describe Puppet::FileServing::Metadata do + it "should should be a subclass of Base" do + Puppet::FileServing::Metadata.superclass.should equal(Puppet::FileServing::Base) + end + + it "should indirect file_metadata" do + Puppet::FileServing::Metadata.indirection.name.should == :file_metadata + end + + it "should should include the IndirectionHooks module in its indirection" do + Puppet::FileServing::Metadata.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks) + end + + it "should have a method that triggers attribute collection" do + Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:collect) + end + + it "should support pson serialization" do + Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:to_pson) + end + + it "should support to_pson_data_hash" do + Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:to_pson_data_hash) + end + + it "should support pson deserialization" do + Puppet::FileServing::Metadata.should respond_to(:from_pson) + end + + describe "when serializing" do + before do + @metadata = Puppet::FileServing::Metadata.new("/foo/bar") + end + it "should perform pson serialization by calling to_pson on it's pson_data_hash" do + pdh = mock "data hash" + pdh_as_pson = mock "data as pson" + @metadata.expects(:to_pson_data_hash).returns pdh + pdh.expects(:to_pson).returns pdh_as_pson + @metadata.to_pson.should == pdh_as_pson + end + + it "should serialize as FileMetadata" do + @metadata.to_pson_data_hash['document_type'].should == "FileMetadata" + end + + it "the data should include the path, relative_path, links, owner, group, mode, checksum, type, and destination" do + @metadata.to_pson_data_hash['data'].keys.sort.should == %w{ path relative_path links owner group mode checksum type destination }.sort + end + + it "should pass the path in the hash verbatum" do + @metadata.to_pson_data_hash['data']['path'] == @metadata.path + end + + it "should pass the relative_path in the hash verbatum" do + @metadata.to_pson_data_hash['data']['relative_path'] == @metadata.relative_path + end + + it "should pass the links in the hash verbatum" do + @metadata.to_pson_data_hash['data']['links'] == @metadata.links + end + + it "should pass the path owner in the hash verbatum" do + @metadata.to_pson_data_hash['data']['owner'] == @metadata.owner + end + + it "should pass the group in the hash verbatum" do + @metadata.to_pson_data_hash['data']['group'] == @metadata.group + end + + it "should pass the mode in the hash verbatum" do + @metadata.to_pson_data_hash['data']['mode'] == @metadata.mode + end + + it "should pass the ftype in the hash verbatum as the 'type'" do + @metadata.to_pson_data_hash['data']['type'] == @metadata.ftype + end + + it "should pass the destination verbatum" do + @metadata.to_pson_data_hash['data']['destination'] == @metadata.destination + end + + it "should pass the checksum in the hash as a nested hash" do + @metadata.to_pson_data_hash['data']['checksum'].should be_is_a(Hash) + end + + it "should pass the checksum_type in the hash verbatum as the checksum's type" do + @metadata.to_pson_data_hash['data']['checksum']['type'] == @metadata.checksum_type + end + + it "should pass the checksum in the hash verbatum as the checksum's value" do + @metadata.to_pson_data_hash['data']['checksum']['value'] == @metadata.checksum + end + + end +end + +describe Puppet::FileServing::Metadata, " when finding the file to use for setting attributes" do + before do + @path = "/my/path" + @metadata = Puppet::FileServing::Metadata.new(@path) + + # Use a link because it's easier to test -- no checksumming + @stat = stub "stat", :uid => 10, :gid => 20, :mode => 0755, :ftype => "link" + + # Not quite. We don't want to checksum links, but we must because they might be being followed. + @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. + @metadata.stubs(:md5_file).returns(@checksum) # + end + + it "should accept a base path path to which the file should be relative" do + File.expects(:lstat).with(@path).returns @stat + File.expects(:readlink).with(@path).returns "/what/ever" + @metadata.collect + end + + it "should use the set base path if one is not provided" do + File.expects(:lstat).with(@path).returns @stat + File.expects(:readlink).with(@path).returns "/what/ever" + @metadata.collect() + end + + it "should raise an exception if the file does not exist" do + File.expects(:lstat).with(@path).raises(Errno::ENOENT) + proc { @metadata.collect()}.should raise_error(Errno::ENOENT) + end +end + +describe Puppet::FileServing::Metadata, " when collecting attributes" do + before do + @path = "/my/file" + # Use a real file mode, so we can validate the masking is done. + @stat = stub 'stat', :uid => 10, :gid => 20, :mode => 33261, :ftype => "file" + File.stubs(:lstat).returns(@stat) + @checksum = Digest::MD5.hexdigest("some content\n") + @metadata = Puppet::FileServing::Metadata.new("/my/file") + @metadata.stubs(:md5_file).returns(@checksum) + @metadata.collect + end + + it "should be able to produce xmlrpc-style attribute information" do + @metadata.should respond_to(:attributes_with_tabs) + end + + # LAK:FIXME This should actually change at some point + it "should set the owner by id" do + @metadata.owner.should be_instance_of(Fixnum) + end + + # LAK:FIXME This should actually change at some point + it "should set the group by id" do + @metadata.group.should be_instance_of(Fixnum) + end + + it "should set the owner to the file's current owner" do + @metadata.owner.should == 10 + end + + it "should set the group to the file's current group" do + @metadata.group.should == 20 + end + + it "should set the mode to the file's masked mode" do + @metadata.mode.should == 0755 + end + + it "should set the checksum to the file's current checksum" do + @metadata.checksum.should == "{md5}" + @checksum + end + + describe "when managing files" do + it "should default to a checksum of type MD5" do + @metadata.checksum.should == "{md5}" + @checksum + end + + it "should give a mtime checksum when checksum_type is set" do + time = Time.now + @metadata.checksum_type = "mtime" + @metadata.expects(:mtime_file).returns(@time) + @metadata.collect + @metadata.checksum.should == "{mtime}" + @time.to_s + end + + it "should produce tab-separated mode, type, owner, group, and checksum for xmlrpc" do + @metadata.attributes_with_tabs.should == "#{0755.to_s}\tfile\t10\t20\t{md5}#{@checksum}" + end + end + + describe "when managing directories" do + before do + @stat.stubs(:ftype).returns("directory") + @time = Time.now + @metadata.expects(:ctime_file).returns(@time) + end + + it "should only use checksums of type 'ctime' for directories" do + @metadata.collect + @metadata.checksum.should == "{ctime}" + @time.to_s + end + + it "should only use checksums of type 'ctime' for directories even if checksum_type set" do + @metadata.checksum_type = "mtime" + @metadata.expects(:mtime_file).never + @metadata.collect + @metadata.checksum.should == "{ctime}" + @time.to_s + end + + it "should produce tab-separated mode, type, owner, group, and checksum for xmlrpc" do + @metadata.collect + @metadata.attributes_with_tabs.should == "#{0755.to_s}\tdirectory\t10\t20\t{ctime}#{@time.to_s}" + end + end + + describe "when managing links" do + before do + @stat.stubs(:ftype).returns("link") + File.expects(:readlink).with("/my/file").returns("/path/to/link") + @metadata.collect + + @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. + @file.stubs(:md5_file).returns(@checksum) # + end + + it "should read links instead of returning their checksums" do + @metadata.destination.should == "/path/to/link" + end + + it "should produce tab-separated mode, type, owner, group, and destination for xmlrpc" do + pending "We'd like this to be true, but we need to always collect the checksum because in the server/client/server round trip we lose the distintion between manage and follow." + @metadata.attributes_with_tabs.should == "#{0755}\tlink\t10\t20\t/path/to/link" + end + + it "should produce tab-separated mode, type, owner, group, checksum, and destination for xmlrpc" do + @metadata.attributes_with_tabs.should == "#{0755}\tlink\t10\t20\t{md5}eb9c2bf0eb63f3a7bc0ea37ef18aeba5\t/path/to/link" + end + end +end + +describe Puppet::FileServing::Metadata, " when pointing to a link" do + describe "when links are managed" do + before do + @file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :manage) + File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755) + File.expects(:readlink).with("/base/path/my/file").returns "/some/other/path" + + @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. + @file.stubs(:md5_file).returns(@checksum) # + end + it "should store the destination of the link in :destination if links are :manage" do + @file.collect + @file.destination.should == "/some/other/path" + end + it "should not collect the checksum if links are :manage" do + pending "We'd like this to be true, but we need to always collect the checksum because in the server/client/server round trip we lose the distintion between manage and follow." + @file.collect + @file.checksum.should be_nil + end + it "should collect the checksum if links are :manage" do # see pending note above + @file.collect + @file.checksum.should == "{md5}#{@checksum}" + end + end + + describe "when links are followed" do + before do + @file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :follow) + File.expects(:stat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "file", :mode => 0755) + File.expects(:readlink).with("/base/path/my/file").never + @checksum = Digest::MD5.hexdigest("some content\n") + @file.stubs(:md5_file).returns(@checksum) + end + it "should not store the destination of the link in :destination if links are :follow" do + @file.collect + @file.destination.should be_nil + end + it "should collect the checksum if links are :follow" do + @file.collect + @file.checksum.should == "{md5}#{@checksum}" + end + end +end diff --git a/spec/unit/file_serving/metadata_spec_spec.rb b/spec/unit/file_serving/metadata_spec_spec.rb deleted file mode 100755 index ef2b3b6f0..000000000 --- a/spec/unit/file_serving/metadata_spec_spec.rb +++ /dev/null @@ -1,286 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/file_serving/metadata' - -describe Puppet::FileServing::Metadata do - it "should should be a subclass of Base" do - Puppet::FileServing::Metadata.superclass.should equal(Puppet::FileServing::Base) - end - - it "should indirect file_metadata" do - Puppet::FileServing::Metadata.indirection.name.should == :file_metadata - end - - it "should should include the IndirectionHooks module in its indirection" do - Puppet::FileServing::Metadata.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks) - end - - it "should have a method that triggers attribute collection" do - Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:collect) - end - - it "should support pson serialization" do - Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:to_pson) - end - - it "should support to_pson_data_hash" do - Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:to_pson_data_hash) - end - - it "should support pson deserialization" do - Puppet::FileServing::Metadata.should respond_to(:from_pson) - end - - describe "when serializing" do - before do - @metadata = Puppet::FileServing::Metadata.new("/foo/bar") - end - it "should perform pson serialization by calling to_pson on it's pson_data_hash" do - pdh = mock "data hash" - pdh_as_pson = mock "data as pson" - @metadata.expects(:to_pson_data_hash).returns pdh - pdh.expects(:to_pson).returns pdh_as_pson - @metadata.to_pson.should == pdh_as_pson - end - - it "should serialize as FileMetadata" do - @metadata.to_pson_data_hash['document_type'].should == "FileMetadata" - end - - it "the data should include the path, relative_path, links, owner, group, mode, checksum, type, and destination" do - @metadata.to_pson_data_hash['data'].keys.sort.should == %w{ path relative_path links owner group mode checksum type destination }.sort - end - - it "should pass the path in the hash verbatum" do - @metadata.to_pson_data_hash['data']['path'] == @metadata.path - end - - it "should pass the relative_path in the hash verbatum" do - @metadata.to_pson_data_hash['data']['relative_path'] == @metadata.relative_path - end - - it "should pass the links in the hash verbatum" do - @metadata.to_pson_data_hash['data']['links'] == @metadata.links - end - - it "should pass the path owner in the hash verbatum" do - @metadata.to_pson_data_hash['data']['owner'] == @metadata.owner - end - - it "should pass the group in the hash verbatum" do - @metadata.to_pson_data_hash['data']['group'] == @metadata.group - end - - it "should pass the mode in the hash verbatum" do - @metadata.to_pson_data_hash['data']['mode'] == @metadata.mode - end - - it "should pass the ftype in the hash verbatum as the 'type'" do - @metadata.to_pson_data_hash['data']['type'] == @metadata.ftype - end - - it "should pass the destination verbatum" do - @metadata.to_pson_data_hash['data']['destination'] == @metadata.destination - end - - it "should pass the checksum in the hash as a nested hash" do - @metadata.to_pson_data_hash['data']['checksum'].should be_is_a(Hash) - end - - it "should pass the checksum_type in the hash verbatum as the checksum's type" do - @metadata.to_pson_data_hash['data']['checksum']['type'] == @metadata.checksum_type - end - - it "should pass the checksum in the hash verbatum as the checksum's value" do - @metadata.to_pson_data_hash['data']['checksum']['value'] == @metadata.checksum - end - - end -end - -describe Puppet::FileServing::Metadata, " when finding the file to use for setting attributes" do - before do - @path = "/my/path" - @metadata = Puppet::FileServing::Metadata.new(@path) - - # Use a link because it's easier to test -- no checksumming - @stat = stub "stat", :uid => 10, :gid => 20, :mode => 0755, :ftype => "link" - - # Not quite. We don't want to checksum links, but we must because they might be being followed. - @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. - @metadata.stubs(:md5_file).returns(@checksum) # - end - - it "should accept a base path path to which the file should be relative" do - File.expects(:lstat).with(@path).returns @stat - File.expects(:readlink).with(@path).returns "/what/ever" - @metadata.collect - end - - it "should use the set base path if one is not provided" do - File.expects(:lstat).with(@path).returns @stat - File.expects(:readlink).with(@path).returns "/what/ever" - @metadata.collect() - end - - it "should raise an exception if the file does not exist" do - File.expects(:lstat).with(@path).raises(Errno::ENOENT) - proc { @metadata.collect()}.should raise_error(Errno::ENOENT) - end -end - -describe Puppet::FileServing::Metadata, " when collecting attributes" do - before do - @path = "/my/file" - # Use a real file mode, so we can validate the masking is done. - @stat = stub 'stat', :uid => 10, :gid => 20, :mode => 33261, :ftype => "file" - File.stubs(:lstat).returns(@stat) - @checksum = Digest::MD5.hexdigest("some content\n") - @metadata = Puppet::FileServing::Metadata.new("/my/file") - @metadata.stubs(:md5_file).returns(@checksum) - @metadata.collect - end - - it "should be able to produce xmlrpc-style attribute information" do - @metadata.should respond_to(:attributes_with_tabs) - end - - # LAK:FIXME This should actually change at some point - it "should set the owner by id" do - @metadata.owner.should be_instance_of(Fixnum) - end - - # LAK:FIXME This should actually change at some point - it "should set the group by id" do - @metadata.group.should be_instance_of(Fixnum) - end - - it "should set the owner to the file's current owner" do - @metadata.owner.should == 10 - end - - it "should set the group to the file's current group" do - @metadata.group.should == 20 - end - - it "should set the mode to the file's masked mode" do - @metadata.mode.should == 0755 - end - - it "should set the checksum to the file's current checksum" do - @metadata.checksum.should == "{md5}" + @checksum - end - - describe "when managing files" do - it "should default to a checksum of type MD5" do - @metadata.checksum.should == "{md5}" + @checksum - end - - it "should give a mtime checksum when checksum_type is set" do - time = Time.now - @metadata.checksum_type = "mtime" - @metadata.expects(:mtime_file).returns(@time) - @metadata.collect - @metadata.checksum.should == "{mtime}" + @time.to_s - end - - it "should produce tab-separated mode, type, owner, group, and checksum for xmlrpc" do - @metadata.attributes_with_tabs.should == "#{0755.to_s}\tfile\t10\t20\t{md5}#{@checksum}" - end - end - - describe "when managing directories" do - before do - @stat.stubs(:ftype).returns("directory") - @time = Time.now - @metadata.expects(:ctime_file).returns(@time) - end - - it "should only use checksums of type 'ctime' for directories" do - @metadata.collect - @metadata.checksum.should == "{ctime}" + @time.to_s - end - - it "should only use checksums of type 'ctime' for directories even if checksum_type set" do - @metadata.checksum_type = "mtime" - @metadata.expects(:mtime_file).never - @metadata.collect - @metadata.checksum.should == "{ctime}" + @time.to_s - end - - it "should produce tab-separated mode, type, owner, group, and checksum for xmlrpc" do - @metadata.collect - @metadata.attributes_with_tabs.should == "#{0755.to_s}\tdirectory\t10\t20\t{ctime}#{@time.to_s}" - end - end - - describe "when managing links" do - before do - @stat.stubs(:ftype).returns("link") - File.expects(:readlink).with("/my/file").returns("/path/to/link") - @metadata.collect - - @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. - @file.stubs(:md5_file).returns(@checksum) # - end - - it "should read links instead of returning their checksums" do - @metadata.destination.should == "/path/to/link" - end - - it "should produce tab-separated mode, type, owner, group, and destination for xmlrpc" do - pending "We'd like this to be true, but we need to always collect the checksum because in the server/client/server round trip we lose the distintion between manage and follow." - @metadata.attributes_with_tabs.should == "#{0755}\tlink\t10\t20\t/path/to/link" - end - - it "should produce tab-separated mode, type, owner, group, checksum, and destination for xmlrpc" do - @metadata.attributes_with_tabs.should == "#{0755}\tlink\t10\t20\t{md5}eb9c2bf0eb63f3a7bc0ea37ef18aeba5\t/path/to/link" - end - end -end - -describe Puppet::FileServing::Metadata, " when pointing to a link" do - describe "when links are managed" do - before do - @file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :manage) - File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755) - File.expects(:readlink).with("/base/path/my/file").returns "/some/other/path" - - @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. - @file.stubs(:md5_file).returns(@checksum) # - end - it "should store the destination of the link in :destination if links are :manage" do - @file.collect - @file.destination.should == "/some/other/path" - end - it "should not collect the checksum if links are :manage" do - pending "We'd like this to be true, but we need to always collect the checksum because in the server/client/server round trip we lose the distintion between manage and follow." - @file.collect - @file.checksum.should be_nil - end - it "should collect the checksum if links are :manage" do # see pending note above - @file.collect - @file.checksum.should == "{md5}#{@checksum}" - end - end - - describe "when links are followed" do - before do - @file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :follow) - File.expects(:stat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "file", :mode => 0755) - File.expects(:readlink).with("/base/path/my/file").never - @checksum = Digest::MD5.hexdigest("some content\n") - @file.stubs(:md5_file).returns(@checksum) - end - it "should not store the destination of the link in :destination if links are :follow" do - @file.collect - @file.destination.should be_nil - end - it "should collect the checksum if links are :follow" do - @file.collect - @file.checksum.should == "{md5}#{@checksum}" - end - end -end diff --git a/spec/unit/file_serving/mount/file_spec.rb b/spec/unit/file_serving/mount/file_spec.rb new file mode 100755 index 000000000..69660d62f --- /dev/null +++ b/spec/unit/file_serving/mount/file_spec.rb @@ -0,0 +1,196 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/file_serving/mount/file' + +module FileServingMountTesting + def stub_facter(hostname) + Facter.stubs(:value).with("hostname").returns(hostname.sub(/\..+/, '')) + Facter.stubs(:value).with("domain").returns(hostname.sub(/^[^.]+\./, '')) + end +end + +describe Puppet::FileServing::Mount::File do + it "should provide a method for clearing its cached host information" do + old = Puppet::FileServing::Mount::File.localmap + Puppet::Util::Cacher.expire + Puppet::FileServing::Mount::File.localmap.should_not equal(old) + end + + it "should be invalid if it does not have a path" do + lambda { Puppet::FileServing::Mount::File.new("foo").validate }.should raise_error(ArgumentError) + end + + it "should be valid if it has a path" do + FileTest.stubs(:directory?).returns true + FileTest.stubs(:readable?).returns true + mount = Puppet::FileServing::Mount::File.new("foo") + mount.path = "/foo" + lambda { mount.validate }.should_not raise_error(ArgumentError) + end +end + +describe Puppet::FileServing::Mount::File, " when setting the path" do + before do + @mount = Puppet::FileServing::Mount::File.new("test") + @dir = "/this/path/does/not/exist" + end + + it "should fail if the path is not a directory" do + FileTest.expects(:directory?).returns(false) + proc { @mount.path = @dir }.should raise_error(ArgumentError) + end + + it "should fail if the path is not readable" do + FileTest.expects(:directory?).returns(true) + FileTest.expects(:readable?).returns(false) + proc { @mount.path = @dir }.should raise_error(ArgumentError) + end +end + +describe Puppet::FileServing::Mount::File, " when substituting hostnames and ip addresses into file paths" do + include FileServingMountTesting + + before do + FileTest.stubs(:directory?).returns(true) + FileTest.stubs(:readable?).returns(true) + @mount = Puppet::FileServing::Mount::File.new("test") + @host = "host.domain.com" + end + + it "should replace incidences of %h in the path with the client's short name" do + @mount.path = "/dir/%h/yay" + @mount.path(@host).should == "/dir/host/yay" + end + + it "should replace incidences of %H in the path with the client's fully qualified name" do + @mount.path = "/dir/%H/yay" + @mount.path(@host).should == "/dir/host.domain.com/yay" + end + + it "should replace incidences of %d in the path with the client's domain name" do + @mount.path = "/dir/%d/yay" + @mount.path(@host).should == "/dir/domain.com/yay" + end + + it "should perform all necessary replacements" do + @mount.path = "/%h/%d/%H" + @mount.path(@host).should == "/host/domain.com/host.domain.com" + end + + it "should use local host information if no client data is provided" do + stub_facter("myhost.mydomain.com") + @mount.path = "/%h/%d/%H" + @mount.path().should == "/myhost/mydomain.com/myhost.mydomain.com" + end + + after do + Puppet::Util::Cacher.expire + end +end + +describe Puppet::FileServing::Mount::File, "when determining the complete file path" do + include FileServingMountTesting + + before do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(true) + FileTest.stubs(:readable?).returns(true) + @mount = Puppet::FileServing::Mount::File.new("test") + @mount.path = "/mount" + stub_facter("myhost.mydomain.com") + @host = "host.domain.com" + end + + it "should return nil if the file is absent" do + FileTest.stubs(:exist?).returns(false) + @mount.complete_path("/my/path", nil).should be_nil + end + + it "should write a log message if the file is absent" do + FileTest.stubs(:exist?).returns(false) + + Puppet.expects(:info).with("File does not exist or is not accessible: /mount/my/path") + + @mount.complete_path("/my/path", nil) + end + + it "should return the file path if the file is present" do + FileTest.stubs(:exist?).with("/my/path").returns(true) + @mount.complete_path("/my/path", nil).should == "/mount/my/path" + end + + it "should treat a nil file name as the path to the mount itself" do + FileTest.stubs(:exist?).returns(true) + @mount.complete_path(nil, nil).should == "/mount" + end + + it "should use the client host name if provided in the options" do + @mount.path = "/mount/%h" + @mount.complete_path("/my/path", @host).should == "/mount/host/my/path" + end + + it "should perform replacements on the base path" do + @mount.path = "/blah/%h" + @mount.complete_path("/my/stuff", @host).should == "/blah/host/my/stuff" + end + + it "should not perform replacements on the per-file path" do + @mount.path = "/blah" + @mount.complete_path("/%h/stuff", @host).should == "/blah/%h/stuff" + end + + it "should look for files relative to its base directory" do + @mount.complete_path("/my/stuff", @host).should == "/mount/my/stuff" + end +end + +describe Puppet::FileServing::Mount::File, "when finding files" do + include FileServingMountTesting + + before do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(true) + FileTest.stubs(:readable?).returns(true) + @mount = Puppet::FileServing::Mount::File.new("test") + @mount.path = "/mount" + stub_facter("myhost.mydomain.com") + @host = "host.domain.com" + + @request = stub 'request', :node => "foo" + end + + it "should return the results of the complete file path" do + FileTest.stubs(:exist?).returns(false) + @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" + @mount.find("/my/path", @request).should == "eh" + end +end + +describe Puppet::FileServing::Mount::File, "when searching for files" do + include FileServingMountTesting + + before do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(true) + FileTest.stubs(:readable?).returns(true) + @mount = Puppet::FileServing::Mount::File.new("test") + @mount.path = "/mount" + stub_facter("myhost.mydomain.com") + @host = "host.domain.com" + + @request = stub 'request', :node => "foo" + end + + it "should return the results of the complete file path as an array" do + FileTest.stubs(:exist?).returns(false) + @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" + @mount.search("/my/path", @request).should == ["eh"] + end + + it "should return nil if the complete path is nil" do + FileTest.stubs(:exist?).returns(false) + @mount.expects(:complete_path).with("/my/path", "foo").returns nil + @mount.search("/my/path", @request).should be_nil + end +end diff --git a/spec/unit/file_serving/mount/file_spec_spec.rb b/spec/unit/file_serving/mount/file_spec_spec.rb deleted file mode 100755 index 69660d62f..000000000 --- a/spec/unit/file_serving/mount/file_spec_spec.rb +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/file_serving/mount/file' - -module FileServingMountTesting - def stub_facter(hostname) - Facter.stubs(:value).with("hostname").returns(hostname.sub(/\..+/, '')) - Facter.stubs(:value).with("domain").returns(hostname.sub(/^[^.]+\./, '')) - end -end - -describe Puppet::FileServing::Mount::File do - it "should provide a method for clearing its cached host information" do - old = Puppet::FileServing::Mount::File.localmap - Puppet::Util::Cacher.expire - Puppet::FileServing::Mount::File.localmap.should_not equal(old) - end - - it "should be invalid if it does not have a path" do - lambda { Puppet::FileServing::Mount::File.new("foo").validate }.should raise_error(ArgumentError) - end - - it "should be valid if it has a path" do - FileTest.stubs(:directory?).returns true - FileTest.stubs(:readable?).returns true - mount = Puppet::FileServing::Mount::File.new("foo") - mount.path = "/foo" - lambda { mount.validate }.should_not raise_error(ArgumentError) - end -end - -describe Puppet::FileServing::Mount::File, " when setting the path" do - before do - @mount = Puppet::FileServing::Mount::File.new("test") - @dir = "/this/path/does/not/exist" - end - - it "should fail if the path is not a directory" do - FileTest.expects(:directory?).returns(false) - proc { @mount.path = @dir }.should raise_error(ArgumentError) - end - - it "should fail if the path is not readable" do - FileTest.expects(:directory?).returns(true) - FileTest.expects(:readable?).returns(false) - proc { @mount.path = @dir }.should raise_error(ArgumentError) - end -end - -describe Puppet::FileServing::Mount::File, " when substituting hostnames and ip addresses into file paths" do - include FileServingMountTesting - - before do - FileTest.stubs(:directory?).returns(true) - FileTest.stubs(:readable?).returns(true) - @mount = Puppet::FileServing::Mount::File.new("test") - @host = "host.domain.com" - end - - it "should replace incidences of %h in the path with the client's short name" do - @mount.path = "/dir/%h/yay" - @mount.path(@host).should == "/dir/host/yay" - end - - it "should replace incidences of %H in the path with the client's fully qualified name" do - @mount.path = "/dir/%H/yay" - @mount.path(@host).should == "/dir/host.domain.com/yay" - end - - it "should replace incidences of %d in the path with the client's domain name" do - @mount.path = "/dir/%d/yay" - @mount.path(@host).should == "/dir/domain.com/yay" - end - - it "should perform all necessary replacements" do - @mount.path = "/%h/%d/%H" - @mount.path(@host).should == "/host/domain.com/host.domain.com" - end - - it "should use local host information if no client data is provided" do - stub_facter("myhost.mydomain.com") - @mount.path = "/%h/%d/%H" - @mount.path().should == "/myhost/mydomain.com/myhost.mydomain.com" - end - - after do - Puppet::Util::Cacher.expire - end -end - -describe Puppet::FileServing::Mount::File, "when determining the complete file path" do - include FileServingMountTesting - - before do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(true) - FileTest.stubs(:readable?).returns(true) - @mount = Puppet::FileServing::Mount::File.new("test") - @mount.path = "/mount" - stub_facter("myhost.mydomain.com") - @host = "host.domain.com" - end - - it "should return nil if the file is absent" do - FileTest.stubs(:exist?).returns(false) - @mount.complete_path("/my/path", nil).should be_nil - end - - it "should write a log message if the file is absent" do - FileTest.stubs(:exist?).returns(false) - - Puppet.expects(:info).with("File does not exist or is not accessible: /mount/my/path") - - @mount.complete_path("/my/path", nil) - end - - it "should return the file path if the file is present" do - FileTest.stubs(:exist?).with("/my/path").returns(true) - @mount.complete_path("/my/path", nil).should == "/mount/my/path" - end - - it "should treat a nil file name as the path to the mount itself" do - FileTest.stubs(:exist?).returns(true) - @mount.complete_path(nil, nil).should == "/mount" - end - - it "should use the client host name if provided in the options" do - @mount.path = "/mount/%h" - @mount.complete_path("/my/path", @host).should == "/mount/host/my/path" - end - - it "should perform replacements on the base path" do - @mount.path = "/blah/%h" - @mount.complete_path("/my/stuff", @host).should == "/blah/host/my/stuff" - end - - it "should not perform replacements on the per-file path" do - @mount.path = "/blah" - @mount.complete_path("/%h/stuff", @host).should == "/blah/%h/stuff" - end - - it "should look for files relative to its base directory" do - @mount.complete_path("/my/stuff", @host).should == "/mount/my/stuff" - end -end - -describe Puppet::FileServing::Mount::File, "when finding files" do - include FileServingMountTesting - - before do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(true) - FileTest.stubs(:readable?).returns(true) - @mount = Puppet::FileServing::Mount::File.new("test") - @mount.path = "/mount" - stub_facter("myhost.mydomain.com") - @host = "host.domain.com" - - @request = stub 'request', :node => "foo" - end - - it "should return the results of the complete file path" do - FileTest.stubs(:exist?).returns(false) - @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" - @mount.find("/my/path", @request).should == "eh" - end -end - -describe Puppet::FileServing::Mount::File, "when searching for files" do - include FileServingMountTesting - - before do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(true) - FileTest.stubs(:readable?).returns(true) - @mount = Puppet::FileServing::Mount::File.new("test") - @mount.path = "/mount" - stub_facter("myhost.mydomain.com") - @host = "host.domain.com" - - @request = stub 'request', :node => "foo" - end - - it "should return the results of the complete file path as an array" do - FileTest.stubs(:exist?).returns(false) - @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" - @mount.search("/my/path", @request).should == ["eh"] - end - - it "should return nil if the complete path is nil" do - FileTest.stubs(:exist?).returns(false) - @mount.expects(:complete_path).with("/my/path", "foo").returns nil - @mount.search("/my/path", @request).should be_nil - end -end diff --git a/spec/unit/file_serving/mount/modules_spec.rb b/spec/unit/file_serving/mount/modules_spec.rb new file mode 100755 index 000000000..eeecc9aae --- /dev/null +++ b/spec/unit/file_serving/mount/modules_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/file_serving/mount/modules' + +describe Puppet::FileServing::Mount::Modules do + before do + @mount = Puppet::FileServing::Mount::Modules.new("modules") + + @environment = stub 'environment', :module => nil + @request = stub 'request', :environment => @environment + end + + describe "when finding files" do + it "should use the provided environment to find the module" do + @environment.expects(:module) + + @mount.find("foo", @request) + end + + it "should treat the first field of the relative path as the module name" do + @environment.expects(:module).with("foo") + @mount.find("foo/bar/baz", @request) + end + + it "should return nil if the specified module does not exist" do + @environment.expects(:module).with("foo").returns nil + @mount.find("foo/bar/baz", @request) + end + + it "should return the file path from the module" do + mod = mock 'module' + mod.expects(:file).with("bar/baz").returns "eh" + @environment.expects(:module).with("foo").returns mod + @mount.find("foo/bar/baz", @request).should == "eh" + end + end + + describe "when searching for files" do + it "should use the node's environment to search the module" do + @environment.expects(:module) + + @mount.search("foo", @request) + end + + it "should treat the first field of the relative path as the module name" do + @environment.expects(:module).with("foo") + @mount.search("foo/bar/baz", @request) + end + + it "should return nil if the specified module does not exist" do + @environment.expects(:module).with("foo").returns nil + @mount.search("foo/bar/baz", @request) + end + + it "should return the file path as an array from the module" do + mod = mock 'module' + mod.expects(:file).with("bar/baz").returns "eh" + @environment.expects(:module).with("foo").returns mod + @mount.search("foo/bar/baz", @request).should == ["eh"] + end + end +end diff --git a/spec/unit/file_serving/mount/modules_spec_spec.rb b/spec/unit/file_serving/mount/modules_spec_spec.rb deleted file mode 100755 index eeecc9aae..000000000 --- a/spec/unit/file_serving/mount/modules_spec_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/file_serving/mount/modules' - -describe Puppet::FileServing::Mount::Modules do - before do - @mount = Puppet::FileServing::Mount::Modules.new("modules") - - @environment = stub 'environment', :module => nil - @request = stub 'request', :environment => @environment - end - - describe "when finding files" do - it "should use the provided environment to find the module" do - @environment.expects(:module) - - @mount.find("foo", @request) - end - - it "should treat the first field of the relative path as the module name" do - @environment.expects(:module).with("foo") - @mount.find("foo/bar/baz", @request) - end - - it "should return nil if the specified module does not exist" do - @environment.expects(:module).with("foo").returns nil - @mount.find("foo/bar/baz", @request) - end - - it "should return the file path from the module" do - mod = mock 'module' - mod.expects(:file).with("bar/baz").returns "eh" - @environment.expects(:module).with("foo").returns mod - @mount.find("foo/bar/baz", @request).should == "eh" - end - end - - describe "when searching for files" do - it "should use the node's environment to search the module" do - @environment.expects(:module) - - @mount.search("foo", @request) - end - - it "should treat the first field of the relative path as the module name" do - @environment.expects(:module).with("foo") - @mount.search("foo/bar/baz", @request) - end - - it "should return nil if the specified module does not exist" do - @environment.expects(:module).with("foo").returns nil - @mount.search("foo/bar/baz", @request) - end - - it "should return the file path as an array from the module" do - mod = mock 'module' - mod.expects(:file).with("bar/baz").returns "eh" - @environment.expects(:module).with("foo").returns mod - @mount.search("foo/bar/baz", @request).should == ["eh"] - end - end -end diff --git a/spec/unit/file_serving/mount/plugins_spec.rb b/spec/unit/file_serving/mount/plugins_spec.rb new file mode 100755 index 000000000..d8c05a2bd --- /dev/null +++ b/spec/unit/file_serving/mount/plugins_spec.rb @@ -0,0 +1,61 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/file_serving/mount/plugins' + +describe Puppet::FileServing::Mount::Plugins do + before do + @mount = Puppet::FileServing::Mount::Plugins.new("plugins") + + @environment = stub 'environment', :module => nil + @request = stub 'request', :environment => @environment + end + + describe "when finding files" do + it "should use the provided environment to find the modules" do + @environment.expects(:modules).returns [] + + @mount.find("foo", @request) + end + + it "should return nil if no module can be found with a matching plugin" do + mod = mock 'module' + mod.stubs(:plugin).with("foo/bar").returns nil + + @environment.stubs(:modules).returns [mod] + @mount.find("foo/bar", @request).should be_nil + end + + it "should return the file path from the module" do + mod = mock 'module' + mod.stubs(:plugin).with("foo/bar").returns "eh" + + @environment.stubs(:modules).returns [mod] + @mount.find("foo/bar", @request).should == "eh" + end + end + + describe "when searching for files" do + it "should use the node's environment to find the modules" do + @environment.expects(:modules).returns [] + + @mount.search("foo", @request) + end + + it "should return nil if no modules can be found that have plugins" do + mod = mock 'module' + mod.stubs(:plugins?).returns false + + @environment.stubs(:modules).returns [] + @mount.search("foo/bar", @request).should be_nil + end + + it "should return the plugin paths for each module that has plugins" do + one = stub 'module', :plugins? => true, :plugin_directory => "/one" + two = stub 'module', :plugins? => true, :plugin_directory => "/two" + + @environment.stubs(:modules).returns [one, two] + @mount.search("foo/bar", @request).should == %w{/one /two} + end + end +end diff --git a/spec/unit/file_serving/mount/plugins_spec_spec.rb b/spec/unit/file_serving/mount/plugins_spec_spec.rb deleted file mode 100755 index d8c05a2bd..000000000 --- a/spec/unit/file_serving/mount/plugins_spec_spec.rb +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/file_serving/mount/plugins' - -describe Puppet::FileServing::Mount::Plugins do - before do - @mount = Puppet::FileServing::Mount::Plugins.new("plugins") - - @environment = stub 'environment', :module => nil - @request = stub 'request', :environment => @environment - end - - describe "when finding files" do - it "should use the provided environment to find the modules" do - @environment.expects(:modules).returns [] - - @mount.find("foo", @request) - end - - it "should return nil if no module can be found with a matching plugin" do - mod = mock 'module' - mod.stubs(:plugin).with("foo/bar").returns nil - - @environment.stubs(:modules).returns [mod] - @mount.find("foo/bar", @request).should be_nil - end - - it "should return the file path from the module" do - mod = mock 'module' - mod.stubs(:plugin).with("foo/bar").returns "eh" - - @environment.stubs(:modules).returns [mod] - @mount.find("foo/bar", @request).should == "eh" - end - end - - describe "when searching for files" do - it "should use the node's environment to find the modules" do - @environment.expects(:modules).returns [] - - @mount.search("foo", @request) - end - - it "should return nil if no modules can be found that have plugins" do - mod = mock 'module' - mod.stubs(:plugins?).returns false - - @environment.stubs(:modules).returns [] - @mount.search("foo/bar", @request).should be_nil - end - - it "should return the plugin paths for each module that has plugins" do - one = stub 'module', :plugins? => true, :plugin_directory => "/one" - two = stub 'module', :plugins? => true, :plugin_directory => "/two" - - @environment.stubs(:modules).returns [one, two] - @mount.search("foo/bar", @request).should == %w{/one /two} - end - end -end diff --git a/spec/unit/file_serving/mount_spec.rb b/spec/unit/file_serving/mount_spec.rb new file mode 100755 index 000000000..cbb97b0d9 --- /dev/null +++ b/spec/unit/file_serving/mount_spec.rb @@ -0,0 +1,32 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/file_serving/mount' + +describe Puppet::FileServing::Mount do + it "should use 'mount[$name]' as its string form" do + Puppet::FileServing::Mount.new("foo").to_s.should == "mount[foo]" + end +end + +describe Puppet::FileServing::Mount, " when initializing" do + it "should fail on non-alphanumeric name" do + proc { Puppet::FileServing::Mount.new("non alpha") }.should raise_error(ArgumentError) + end + + it "should allow dashes in its name" do + Puppet::FileServing::Mount.new("non-alpha").name.should == "non-alpha" + end +end + +describe Puppet::FileServing::Mount, " when finding files" do + it "should fail" do + lambda { Puppet::FileServing::Mount.new("test").find("foo", :one => "two") }.should raise_error(NotImplementedError) + end +end + +describe Puppet::FileServing::Mount, " when searching for files" do + it "should fail" do + lambda { Puppet::FileServing::Mount.new("test").search("foo", :one => "two") }.should raise_error(NotImplementedError) + end +end diff --git a/spec/unit/file_serving/mount_spec_spec.rb b/spec/unit/file_serving/mount_spec_spec.rb deleted file mode 100755 index cbb97b0d9..000000000 --- a/spec/unit/file_serving/mount_spec_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/file_serving/mount' - -describe Puppet::FileServing::Mount do - it "should use 'mount[$name]' as its string form" do - Puppet::FileServing::Mount.new("foo").to_s.should == "mount[foo]" - end -end - -describe Puppet::FileServing::Mount, " when initializing" do - it "should fail on non-alphanumeric name" do - proc { Puppet::FileServing::Mount.new("non alpha") }.should raise_error(ArgumentError) - end - - it "should allow dashes in its name" do - Puppet::FileServing::Mount.new("non-alpha").name.should == "non-alpha" - end -end - -describe Puppet::FileServing::Mount, " when finding files" do - it "should fail" do - lambda { Puppet::FileServing::Mount.new("test").find("foo", :one => "two") }.should raise_error(NotImplementedError) - end -end - -describe Puppet::FileServing::Mount, " when searching for files" do - it "should fail" do - lambda { Puppet::FileServing::Mount.new("test").search("foo", :one => "two") }.should raise_error(NotImplementedError) - end -end diff --git a/spec/unit/file_serving/terminus_helper_spec.rb b/spec/unit/file_serving/terminus_helper_spec.rb new file mode 100755 index 000000000..98c64b712 --- /dev/null +++ b/spec/unit/file_serving/terminus_helper_spec.rb @@ -0,0 +1,98 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-10-22. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/file_serving/terminus_helper' + +describe Puppet::FileServing::TerminusHelper do + before do + @helper = Object.new + @helper.extend(Puppet::FileServing::TerminusHelper) + + @model = mock 'model' + @helper.stubs(:model).returns(@model) + + @request = stub 'request', :key => "url", :options => {} + + @fileset = stub 'fileset', :files => [], :path => "/my/file" + Puppet::FileServing::Fileset.stubs(:new).with("/my/file", {}).returns(@fileset) + end + + it "should use a fileset to find paths" do + @fileset = stub 'fileset', :files => [], :path => "/my/files" + Puppet::FileServing::Fileset.expects(:new).with { |key, options| key == "/my/file" }.returns(@fileset) + @helper.path2instances(@request, "/my/file") + end + + it "should support finding across multiple paths by merging the filesets" do + first = stub 'fileset', :files => [], :path => "/first/file" + Puppet::FileServing::Fileset.expects(:new).with { |path, options| path == "/first/file" }.returns(first) + second = stub 'fileset', :files => [], :path => "/second/file" + Puppet::FileServing::Fileset.expects(:new).with { |path, options| path == "/second/file" }.returns(second) + + Puppet::FileServing::Fileset.expects(:merge).with(first, second).returns({}) + + @helper.path2instances(@request, "/first/file", "/second/file") + end + + it "should pass the indirection request to the Fileset at initialization" do + Puppet::FileServing::Fileset.expects(:new).with { |path, options| options == @request }.returns @fileset + @helper.path2instances(@request, "/my/file") + end + + describe "when creating instances" do + before do + @request.stubs(:key).returns "puppet://host/mount/dir" + + @one = stub 'one', :links= => nil, :collect => nil + @two = stub 'two', :links= => nil, :collect => nil + + @fileset = stub 'fileset', :files => %w{one two}, :path => "/my/file" + Puppet::FileServing::Fileset.stubs(:new).returns(@fileset) + end + + it "should set each returned instance's path to the original path" do + @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@one) + @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@two) + @helper.path2instances(@request, "/my/file") + end + + it "should set each returned instance's relative path to the file-specific path" do + @model.expects(:new).with { |key, options| options[:relative_path] == "one" }.returns(@one) + @model.expects(:new).with { |key, options| options[:relative_path] == "two" }.returns(@two) + @helper.path2instances(@request, "/my/file") + end + + it "should set the links value on each instance if one is provided" do + @one.expects(:links=).with :manage + @two.expects(:links=).with :manage + @model.expects(:new).returns(@one) + @model.expects(:new).returns(@two) + + @request.options[:links] = :manage + @helper.path2instances(@request, "/my/file") + end + + it "should set the request checksum_type if one is provided" do + @one.expects(:checksum_type=).with :test + @two.expects(:checksum_type=).with :test + @model.expects(:new).returns(@one) + @model.expects(:new).returns(@two) + + @request.options[:checksum_type] = :test + @helper.path2instances(@request, "/my/file") + end + + it "should collect the instance's attributes" do + @one.expects(:collect) + @two.expects(:collect) + @model.expects(:new).returns(@one) + @model.expects(:new).returns(@two) + + @helper.path2instances(@request, "/my/file") + end + end +end diff --git a/spec/unit/file_serving/terminus_helper_spec_spec.rb b/spec/unit/file_serving/terminus_helper_spec_spec.rb deleted file mode 100755 index 98c64b712..000000000 --- a/spec/unit/file_serving/terminus_helper_spec_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-10-22. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/file_serving/terminus_helper' - -describe Puppet::FileServing::TerminusHelper do - before do - @helper = Object.new - @helper.extend(Puppet::FileServing::TerminusHelper) - - @model = mock 'model' - @helper.stubs(:model).returns(@model) - - @request = stub 'request', :key => "url", :options => {} - - @fileset = stub 'fileset', :files => [], :path => "/my/file" - Puppet::FileServing::Fileset.stubs(:new).with("/my/file", {}).returns(@fileset) - end - - it "should use a fileset to find paths" do - @fileset = stub 'fileset', :files => [], :path => "/my/files" - Puppet::FileServing::Fileset.expects(:new).with { |key, options| key == "/my/file" }.returns(@fileset) - @helper.path2instances(@request, "/my/file") - end - - it "should support finding across multiple paths by merging the filesets" do - first = stub 'fileset', :files => [], :path => "/first/file" - Puppet::FileServing::Fileset.expects(:new).with { |path, options| path == "/first/file" }.returns(first) - second = stub 'fileset', :files => [], :path => "/second/file" - Puppet::FileServing::Fileset.expects(:new).with { |path, options| path == "/second/file" }.returns(second) - - Puppet::FileServing::Fileset.expects(:merge).with(first, second).returns({}) - - @helper.path2instances(@request, "/first/file", "/second/file") - end - - it "should pass the indirection request to the Fileset at initialization" do - Puppet::FileServing::Fileset.expects(:new).with { |path, options| options == @request }.returns @fileset - @helper.path2instances(@request, "/my/file") - end - - describe "when creating instances" do - before do - @request.stubs(:key).returns "puppet://host/mount/dir" - - @one = stub 'one', :links= => nil, :collect => nil - @two = stub 'two', :links= => nil, :collect => nil - - @fileset = stub 'fileset', :files => %w{one two}, :path => "/my/file" - Puppet::FileServing::Fileset.stubs(:new).returns(@fileset) - end - - it "should set each returned instance's path to the original path" do - @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@one) - @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@two) - @helper.path2instances(@request, "/my/file") - end - - it "should set each returned instance's relative path to the file-specific path" do - @model.expects(:new).with { |key, options| options[:relative_path] == "one" }.returns(@one) - @model.expects(:new).with { |key, options| options[:relative_path] == "two" }.returns(@two) - @helper.path2instances(@request, "/my/file") - end - - it "should set the links value on each instance if one is provided" do - @one.expects(:links=).with :manage - @two.expects(:links=).with :manage - @model.expects(:new).returns(@one) - @model.expects(:new).returns(@two) - - @request.options[:links] = :manage - @helper.path2instances(@request, "/my/file") - end - - it "should set the request checksum_type if one is provided" do - @one.expects(:checksum_type=).with :test - @two.expects(:checksum_type=).with :test - @model.expects(:new).returns(@one) - @model.expects(:new).returns(@two) - - @request.options[:checksum_type] = :test - @helper.path2instances(@request, "/my/file") - end - - it "should collect the instance's attributes" do - @one.expects(:collect) - @two.expects(:collect) - @model.expects(:new).returns(@one) - @model.expects(:new).returns(@two) - - @helper.path2instances(@request, "/my/file") - end - end -end diff --git a/spec/unit/indirector/active_record_spec.rb b/spec/unit/indirector/active_record_spec.rb new file mode 100755 index 000000000..f0bf07f07 --- /dev/null +++ b/spec/unit/indirector/active_record_spec.rb @@ -0,0 +1,76 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/indirector/active_record' + +describe Puppet::Indirector::ActiveRecord do + before do + Puppet::Rails.stubs(:init) + + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @model = mock 'model' + @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model + Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) + + @active_record_class = Class.new(Puppet::Indirector::ActiveRecord) do + def self.to_s + "Mystuff::Testing" + end + end + + @ar_model = mock 'ar_model' + + @active_record_class.use_ar_model @ar_model + @terminus = @active_record_class.new + + @name = "me" + @instance = stub 'instance', :name => @name + + @request = stub 'request', :key => @name, :instance => @instance + end + + it "should allow declaration of an ActiveRecord model to use" do + @active_record_class.use_ar_model "foo" + @active_record_class.ar_model.should == "foo" + end + + describe "when initializing" do + it "should init Rails" do + Puppet::Rails.expects(:init) + @active_record_class.new + end + end + + describe "when finding an instance" do + it "should use the ActiveRecord model to find the instance" do + @ar_model.expects(:find_by_name).with(@name) + + @terminus.find(@request) + end + + it "should return nil if no instance is found" do + @ar_model.expects(:find_by_name).with(@name).returns nil + @terminus.find(@request).should be_nil + end + + it "should convert the instance to a Puppet object if it is found" do + instance = mock 'rails_instance' + instance.expects(:to_puppet).returns "mypuppet" + + @ar_model.expects(:find_by_name).with(@name).returns instance + @terminus.find(@request).should == "mypuppet" + end + end + + describe "when saving an instance" do + it "should use the ActiveRecord model to convert the instance into a Rails object and then save that rails object" do + rails_object = mock 'rails_object' + @ar_model.expects(:from_puppet).with(@instance).returns rails_object + + rails_object.expects(:save) + + @terminus.save(@request) + end + end +end diff --git a/spec/unit/indirector/active_record_spec_spec.rb b/spec/unit/indirector/active_record_spec_spec.rb deleted file mode 100755 index f0bf07f07..000000000 --- a/spec/unit/indirector/active_record_spec_spec.rb +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/indirector/active_record' - -describe Puppet::Indirector::ActiveRecord do - before do - Puppet::Rails.stubs(:init) - - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @model = mock 'model' - @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model - Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - - @active_record_class = Class.new(Puppet::Indirector::ActiveRecord) do - def self.to_s - "Mystuff::Testing" - end - end - - @ar_model = mock 'ar_model' - - @active_record_class.use_ar_model @ar_model - @terminus = @active_record_class.new - - @name = "me" - @instance = stub 'instance', :name => @name - - @request = stub 'request', :key => @name, :instance => @instance - end - - it "should allow declaration of an ActiveRecord model to use" do - @active_record_class.use_ar_model "foo" - @active_record_class.ar_model.should == "foo" - end - - describe "when initializing" do - it "should init Rails" do - Puppet::Rails.expects(:init) - @active_record_class.new - end - end - - describe "when finding an instance" do - it "should use the ActiveRecord model to find the instance" do - @ar_model.expects(:find_by_name).with(@name) - - @terminus.find(@request) - end - - it "should return nil if no instance is found" do - @ar_model.expects(:find_by_name).with(@name).returns nil - @terminus.find(@request).should be_nil - end - - it "should convert the instance to a Puppet object if it is found" do - instance = mock 'rails_instance' - instance.expects(:to_puppet).returns "mypuppet" - - @ar_model.expects(:find_by_name).with(@name).returns instance - @terminus.find(@request).should == "mypuppet" - end - end - - describe "when saving an instance" do - it "should use the ActiveRecord model to convert the instance into a Rails object and then save that rails object" do - rails_object = mock 'rails_object' - @ar_model.expects(:from_puppet).with(@instance).returns rails_object - - rails_object.expects(:save) - - @terminus.save(@request) - end - end -end diff --git a/spec/unit/indirector/catalog/active_record_spec.rb b/spec/unit/indirector/catalog/active_record_spec.rb new file mode 100755 index 000000000..8678dd6b3 --- /dev/null +++ b/spec/unit/indirector/catalog/active_record_spec.rb @@ -0,0 +1,141 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + + +describe "Puppet::Resource::Catalog::ActiveRecord" do + confine "Missing Rails" => Puppet.features.rails? + + before do + require 'puppet/indirector/catalog/active_record' + Puppet.features.stubs(:rails?).returns true + Puppet::Rails.stubs(:init) + @terminus = Puppet::Resource::Catalog::ActiveRecord.new + end + + it "should be a subclass of the ActiveRecord terminus class" do + Puppet::Resource::Catalog::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) + end + + it "should use Puppet::Rails::Host as its ActiveRecord model" do + Puppet::Resource::Catalog::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) + end + + describe "when finding an instance" do + before do + @request = stub 'request', :key => "foo", :options => {:cache_integration_hack => true} + end + + # This hack is here because we don't want to look in the db unless we actually want + # to look in the db, but our indirection architecture in 0.24.x isn't flexible + # enough to tune that via configuration. + it "should return nil unless ':cache_integration_hack' is set to true" do + @request.options[:cache_integration_hack] = false + Puppet::Rails::Host.expects(:find_by_name).never + @terminus.find(@request).should be_nil + end + + it "should use the Hosts ActiveRecord class to find the host" do + Puppet::Rails::Host.expects(:find_by_name).with { |key, args| key == "foo" } + @terminus.find(@request) + end + + it "should return nil if no host instance can be found" do + Puppet::Rails::Host.expects(:find_by_name).returns nil + + @terminus.find(@request).should be_nil + end + + it "should return a catalog with the same name as the host if the host can be found" do + host = stub 'host', :name => "foo", :resources => [] + Puppet::Rails::Host.expects(:find_by_name).returns host + + result = @terminus.find(@request) + result.should be_instance_of(Puppet::Resource::Catalog) + result.name.should == "foo" + end + + it "should set each of the host's resources as a transportable resource within the catalog" do + host = stub 'host', :name => "foo" + Puppet::Rails::Host.expects(:find_by_name).returns host + + res1 = mock 'res1', :to_transportable => "trans_res1" + res2 = mock 'res2', :to_transportable => "trans_res2" + + host.expects(:resources).returns [res1, res2] + + catalog = stub 'catalog' + Puppet::Resource::Catalog.expects(:new).returns catalog + + catalog.expects(:add_resource).with "trans_res1" + catalog.expects(:add_resource).with "trans_res2" + + @terminus.find(@request) + end + end + + describe "when saving an instance" do + before do + @host = stub 'host', :name => "foo", :save => nil, :merge_resources => nil, :last_compile= => nil, :ip= => nil, :environment= => nil + @host.stubs(:railsmark).yields + + @node = stub_everything 'node', :parameters => {} + Puppet::Node.stubs(:find).returns(@node) + + Puppet::Rails::Host.stubs(:find_by_name).returns @host + @catalog = Puppet::Resource::Catalog.new("foo") + @request = stub 'request', :key => "foo", :instance => @catalog + end + + it "should find the Rails host with the same name" do + Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host + + @terminus.save(@request) + end + + it "should create a new Rails host if none can be found" do + Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil + + Puppet::Rails::Host.expects(:create).with(:name => "foo").returns @host + + @terminus.save(@request) + end + + it "should set the catalog vertices as resources on the Rails host instance" do + @catalog.expects(:vertices).returns "foo" + @host.expects(:merge_resources).with("foo") + + @terminus.save(@request) + end + + it "should set host ip if we could find a matching node" do + @node.stubs(:parameters).returns({"ipaddress" => "192.168.0.1"}) + + @host.expects(:ip=).with '192.168.0.1' + + @terminus.save(@request) + end + + it "should set host environment if we could find a matching node" do + @node.stubs(:environment).returns("myenv") + + @host.expects(:environment=).with 'myenv' + + @terminus.save(@request) + end + + it "should set the last compile time on the host" do + now = Time.now + Time.expects(:now).returns now + @host.expects(:last_compile=).with now + + @terminus.save(@request) + end + + it "should save the Rails host instance" do + @host.expects(:save) + + @terminus.save(@request) + end + end +end diff --git a/spec/unit/indirector/catalog/active_record_spec_spec.rb b/spec/unit/indirector/catalog/active_record_spec_spec.rb deleted file mode 100755 index 8678dd6b3..000000000 --- a/spec/unit/indirector/catalog/active_record_spec_spec.rb +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - - -describe "Puppet::Resource::Catalog::ActiveRecord" do - confine "Missing Rails" => Puppet.features.rails? - - before do - require 'puppet/indirector/catalog/active_record' - Puppet.features.stubs(:rails?).returns true - Puppet::Rails.stubs(:init) - @terminus = Puppet::Resource::Catalog::ActiveRecord.new - end - - it "should be a subclass of the ActiveRecord terminus class" do - Puppet::Resource::Catalog::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) - end - - it "should use Puppet::Rails::Host as its ActiveRecord model" do - Puppet::Resource::Catalog::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) - end - - describe "when finding an instance" do - before do - @request = stub 'request', :key => "foo", :options => {:cache_integration_hack => true} - end - - # This hack is here because we don't want to look in the db unless we actually want - # to look in the db, but our indirection architecture in 0.24.x isn't flexible - # enough to tune that via configuration. - it "should return nil unless ':cache_integration_hack' is set to true" do - @request.options[:cache_integration_hack] = false - Puppet::Rails::Host.expects(:find_by_name).never - @terminus.find(@request).should be_nil - end - - it "should use the Hosts ActiveRecord class to find the host" do - Puppet::Rails::Host.expects(:find_by_name).with { |key, args| key == "foo" } - @terminus.find(@request) - end - - it "should return nil if no host instance can be found" do - Puppet::Rails::Host.expects(:find_by_name).returns nil - - @terminus.find(@request).should be_nil - end - - it "should return a catalog with the same name as the host if the host can be found" do - host = stub 'host', :name => "foo", :resources => [] - Puppet::Rails::Host.expects(:find_by_name).returns host - - result = @terminus.find(@request) - result.should be_instance_of(Puppet::Resource::Catalog) - result.name.should == "foo" - end - - it "should set each of the host's resources as a transportable resource within the catalog" do - host = stub 'host', :name => "foo" - Puppet::Rails::Host.expects(:find_by_name).returns host - - res1 = mock 'res1', :to_transportable => "trans_res1" - res2 = mock 'res2', :to_transportable => "trans_res2" - - host.expects(:resources).returns [res1, res2] - - catalog = stub 'catalog' - Puppet::Resource::Catalog.expects(:new).returns catalog - - catalog.expects(:add_resource).with "trans_res1" - catalog.expects(:add_resource).with "trans_res2" - - @terminus.find(@request) - end - end - - describe "when saving an instance" do - before do - @host = stub 'host', :name => "foo", :save => nil, :merge_resources => nil, :last_compile= => nil, :ip= => nil, :environment= => nil - @host.stubs(:railsmark).yields - - @node = stub_everything 'node', :parameters => {} - Puppet::Node.stubs(:find).returns(@node) - - Puppet::Rails::Host.stubs(:find_by_name).returns @host - @catalog = Puppet::Resource::Catalog.new("foo") - @request = stub 'request', :key => "foo", :instance => @catalog - end - - it "should find the Rails host with the same name" do - Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host - - @terminus.save(@request) - end - - it "should create a new Rails host if none can be found" do - Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil - - Puppet::Rails::Host.expects(:create).with(:name => "foo").returns @host - - @terminus.save(@request) - end - - it "should set the catalog vertices as resources on the Rails host instance" do - @catalog.expects(:vertices).returns "foo" - @host.expects(:merge_resources).with("foo") - - @terminus.save(@request) - end - - it "should set host ip if we could find a matching node" do - @node.stubs(:parameters).returns({"ipaddress" => "192.168.0.1"}) - - @host.expects(:ip=).with '192.168.0.1' - - @terminus.save(@request) - end - - it "should set host environment if we could find a matching node" do - @node.stubs(:environment).returns("myenv") - - @host.expects(:environment=).with 'myenv' - - @terminus.save(@request) - end - - it "should set the last compile time on the host" do - now = Time.now - Time.expects(:now).returns now - @host.expects(:last_compile=).with now - - @terminus.save(@request) - end - - it "should save the Rails host instance" do - @host.expects(:save) - - @terminus.save(@request) - end - end -end diff --git a/spec/unit/indirector/catalog/compiler_spec.rb b/spec/unit/indirector/catalog/compiler_spec.rb new file mode 100755 index 000000000..8339d1861 --- /dev/null +++ b/spec/unit/indirector/catalog/compiler_spec.rb @@ -0,0 +1,265 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-9-23. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/catalog/compiler' + +describe Puppet::Resource::Catalog::Compiler do + before do + Puppet::Rails.stubs(:init) + Facter.stubs(:to_hash).returns({}) + Facter.stubs(:value).returns(Facter::Util::Fact.new("something")) + end + + describe "when initializing" do + before do + Puppet.expects(:version).returns(1) + Facter.expects(:value).with('fqdn').returns("my.server.com") + Facter.expects(:value).with('ipaddress').returns("my.ip.address") + end + + it "should gather data about itself" do + Puppet::Resource::Catalog::Compiler.new + end + + it "should cache the server metadata and reuse it" do + compiler = Puppet::Resource::Catalog::Compiler.new + node1 = stub 'node1', :merge => nil + node2 = stub 'node2', :merge => nil + compiler.stubs(:compile) + Puppet::Node.stubs(:find).with('node1').returns(node1) + Puppet::Node.stubs(:find).with('node2').returns(node2) + + compiler.find(stub('request', :node => 'node1', :options => {})) + compiler.find(stub('node2request', :node => 'node2', :options => {})) + end + + it "should provide a method for determining if the catalog is networked" do + compiler = Puppet::Resource::Catalog::Compiler.new + compiler.should respond_to(:networked?) + end + + describe "and storeconfigs is enabled" do + before do + Puppet.settings.expects(:value).with(:storeconfigs).returns true + end + + it "should initialize Rails if it is available" do + Puppet.features.expects(:rails?).returns true + Puppet::Rails.expects(:init) + Puppet::Resource::Catalog::Compiler.new + end + + it "should fail if Rails is unavailable" do + Puppet.features.expects(:rails?).returns false + Puppet::Rails.expects(:init).never + lambda { Puppet::Resource::Catalog::Compiler.new }.should raise_error(Puppet::Error) + end + end + end + + describe "when finding catalogs" do + before do + Facter.stubs(:value).returns("whatever") + + @compiler = Puppet::Resource::Catalog::Compiler.new + @name = "me" + @node = Puppet::Node.new @name + @node.stubs(:merge) + Puppet::Node.stubs(:find).returns @node + @request = stub 'request', :key => "does not matter", :node => @name, :options => {} + end + + it "should directly use provided nodes" do + Puppet::Node.expects(:find).never + @compiler.expects(:compile).with(@node) + @request.stubs(:options).returns(:use_node => @node) + @compiler.find(@request) + end + + it "should use the request's node name if no explicit node is provided" do + Puppet::Node.expects(:find).with(@name).returns(@node) + @compiler.expects(:compile).with(@node) + @compiler.find(@request) + end + + it "should use the provided node name if no explicit node is provided and no authenticated node information is available" do + @request.expects(:node).returns nil + @request.expects(:key).returns "my_node" + + Puppet::Node.expects(:find).with("my_node").returns @node + @compiler.expects(:compile).with(@node) + @compiler.find(@request) + end + + it "should fail if no node is passed and none can be found" do + Puppet::Node.stubs(:find).with(@name).returns(nil) + proc { @compiler.find(@request) }.should raise_error(ArgumentError) + end + + it "should fail intelligently when searching for a node raises an exception" do + Puppet::Node.stubs(:find).with(@name).raises "eh" + proc { @compiler.find(@request) }.should raise_error(Puppet::Error) + end + + it "should pass the found node to the compiler for compiling" do + Puppet::Node.expects(:find).with(@name).returns(@node) + config = mock 'config' + Puppet::Parser::Compiler.expects(:compile).with(@node) + @compiler.find(@request) + end + + it "should extract and save any facts from the request" do + Puppet::Node.expects(:find).with(@name).returns @node + @compiler.expects(:extract_facts_from_request).with(@request) + Puppet::Parser::Compiler.stubs(:compile) + @compiler.find(@request) + end + + it "should return the results of compiling as the catalog" do + Puppet::Node.stubs(:find).returns(@node) + config = mock 'config' + result = mock 'result' + + Puppet::Parser::Compiler.expects(:compile).returns result + @compiler.find(@request).should equal(result) + end + + it "should benchmark the compile process" do + Puppet::Node.stubs(:find).returns(@node) + @compiler.stubs(:networked?).returns(true) + @compiler.expects(:benchmark).with do |level, message| + level == :notice and message =~ /^Compiled catalog/ + end + Puppet::Parser::Compiler.stubs(:compile) + @compiler.find(@request) + end + end + + describe "when extracting facts from the request" do + before do + Facter.stubs(:value).returns "something" + @compiler = Puppet::Resource::Catalog::Compiler.new + @request = stub 'request', :options => {} + + @facts = stub 'facts', :save => nil + end + + it "should do nothing if no facts are provided" do + Puppet::Node::Facts.expects(:convert_from).never + @request.options[:facts] = nil + + @compiler.extract_facts_from_request(@request) + end + + it "should use the Facts class to deserialize the provided facts" do + @request.options[:facts_format] = "foo" + @request.options[:facts] = "bar" + Puppet::Node::Facts.expects(:convert_from).returns @facts + + @compiler.extract_facts_from_request(@request) + end + + it "should use the provided fact format" do + @request.options[:facts_format] = "foo" + @request.options[:facts] = "bar" + Puppet::Node::Facts.expects(:convert_from).with { |format, text| format == "foo" }.returns @facts + + @compiler.extract_facts_from_request(@request) + end + + it "should convert the facts into a fact instance and save it" do + @request.options[:facts_format] = "foo" + @request.options[:facts] = "bar" + Puppet::Node::Facts.expects(:convert_from).returns @facts + + @facts.expects(:save) + + @compiler.extract_facts_from_request(@request) + end + end + + describe "when finding nodes" do + before do + Facter.stubs(:value).returns("whatever") + @compiler = Puppet::Resource::Catalog::Compiler.new + @name = "me" + @node = mock 'node' + @request = stub 'request', :node => @name, :options => {} + @compiler.stubs(:compile) + end + + it "should look node information up via the Node class with the provided key" do + @node.stubs :merge + Puppet::Node.expects(:find).with(@name).returns(@node) + @compiler.find(@request) + end + end + + describe "after finding nodes" do + before do + Puppet.expects(:version).returns(1) + Facter.expects(:value).with('fqdn').returns("my.server.com") + Facter.expects(:value).with('ipaddress').returns("my.ip.address") + @compiler = Puppet::Resource::Catalog::Compiler.new + @name = "me" + @node = mock 'node' + @request = stub 'request', :node => @name, :options => {} + @compiler.stubs(:compile) + Puppet::Node.stubs(:find).with(@name).returns(@node) + end + + it "should add the server's Puppet version to the node's parameters as 'serverversion'" do + @node.expects(:merge).with { |args| args["serverversion"] == "1" } + @compiler.find(@request) + end + + it "should add the server's fqdn to the node's parameters as 'servername'" do + @node.expects(:merge).with { |args| args["servername"] == "my.server.com" } + @compiler.find(@request) + end + + it "should add the server's IP address to the node's parameters as 'serverip'" do + @node.expects(:merge).with { |args| args["serverip"] == "my.ip.address" } + @compiler.find(@request) + end + end + + describe "when filtering resources" do + before :each do + Facter.stubs(:value) + @compiler = Puppet::Resource::Catalog::Compiler.new + @catalog = stub_everything 'catalog' + @catalog.stubs(:respond_to?).with(:filter).returns(true) + end + + it "should delegate to the catalog instance filtering" do + @catalog.expects(:filter) + @compiler.filter(@catalog) + end + + it "should filter out virtual resources" do + resource = mock 'resource', :virtual? => true + @catalog.stubs(:filter).yields(resource) + + @compiler.filter(@catalog) + end + + it "should return the same catalog if it doesn't support filtering" do + @catalog.stubs(:respond_to?).with(:filter).returns(false) + + @compiler.filter(@catalog).should == @catalog + end + + it "should return the filtered catalog" do + catalog = stub 'filtered catalog' + @catalog.stubs(:filter).returns(catalog) + + @compiler.filter(@catalog).should == catalog + end + + end +end diff --git a/spec/unit/indirector/catalog/compiler_spec_spec.rb b/spec/unit/indirector/catalog/compiler_spec_spec.rb deleted file mode 100755 index 8339d1861..000000000 --- a/spec/unit/indirector/catalog/compiler_spec_spec.rb +++ /dev/null @@ -1,265 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-9-23. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/catalog/compiler' - -describe Puppet::Resource::Catalog::Compiler do - before do - Puppet::Rails.stubs(:init) - Facter.stubs(:to_hash).returns({}) - Facter.stubs(:value).returns(Facter::Util::Fact.new("something")) - end - - describe "when initializing" do - before do - Puppet.expects(:version).returns(1) - Facter.expects(:value).with('fqdn').returns("my.server.com") - Facter.expects(:value).with('ipaddress').returns("my.ip.address") - end - - it "should gather data about itself" do - Puppet::Resource::Catalog::Compiler.new - end - - it "should cache the server metadata and reuse it" do - compiler = Puppet::Resource::Catalog::Compiler.new - node1 = stub 'node1', :merge => nil - node2 = stub 'node2', :merge => nil - compiler.stubs(:compile) - Puppet::Node.stubs(:find).with('node1').returns(node1) - Puppet::Node.stubs(:find).with('node2').returns(node2) - - compiler.find(stub('request', :node => 'node1', :options => {})) - compiler.find(stub('node2request', :node => 'node2', :options => {})) - end - - it "should provide a method for determining if the catalog is networked" do - compiler = Puppet::Resource::Catalog::Compiler.new - compiler.should respond_to(:networked?) - end - - describe "and storeconfigs is enabled" do - before do - Puppet.settings.expects(:value).with(:storeconfigs).returns true - end - - it "should initialize Rails if it is available" do - Puppet.features.expects(:rails?).returns true - Puppet::Rails.expects(:init) - Puppet::Resource::Catalog::Compiler.new - end - - it "should fail if Rails is unavailable" do - Puppet.features.expects(:rails?).returns false - Puppet::Rails.expects(:init).never - lambda { Puppet::Resource::Catalog::Compiler.new }.should raise_error(Puppet::Error) - end - end - end - - describe "when finding catalogs" do - before do - Facter.stubs(:value).returns("whatever") - - @compiler = Puppet::Resource::Catalog::Compiler.new - @name = "me" - @node = Puppet::Node.new @name - @node.stubs(:merge) - Puppet::Node.stubs(:find).returns @node - @request = stub 'request', :key => "does not matter", :node => @name, :options => {} - end - - it "should directly use provided nodes" do - Puppet::Node.expects(:find).never - @compiler.expects(:compile).with(@node) - @request.stubs(:options).returns(:use_node => @node) - @compiler.find(@request) - end - - it "should use the request's node name if no explicit node is provided" do - Puppet::Node.expects(:find).with(@name).returns(@node) - @compiler.expects(:compile).with(@node) - @compiler.find(@request) - end - - it "should use the provided node name if no explicit node is provided and no authenticated node information is available" do - @request.expects(:node).returns nil - @request.expects(:key).returns "my_node" - - Puppet::Node.expects(:find).with("my_node").returns @node - @compiler.expects(:compile).with(@node) - @compiler.find(@request) - end - - it "should fail if no node is passed and none can be found" do - Puppet::Node.stubs(:find).with(@name).returns(nil) - proc { @compiler.find(@request) }.should raise_error(ArgumentError) - end - - it "should fail intelligently when searching for a node raises an exception" do - Puppet::Node.stubs(:find).with(@name).raises "eh" - proc { @compiler.find(@request) }.should raise_error(Puppet::Error) - end - - it "should pass the found node to the compiler for compiling" do - Puppet::Node.expects(:find).with(@name).returns(@node) - config = mock 'config' - Puppet::Parser::Compiler.expects(:compile).with(@node) - @compiler.find(@request) - end - - it "should extract and save any facts from the request" do - Puppet::Node.expects(:find).with(@name).returns @node - @compiler.expects(:extract_facts_from_request).with(@request) - Puppet::Parser::Compiler.stubs(:compile) - @compiler.find(@request) - end - - it "should return the results of compiling as the catalog" do - Puppet::Node.stubs(:find).returns(@node) - config = mock 'config' - result = mock 'result' - - Puppet::Parser::Compiler.expects(:compile).returns result - @compiler.find(@request).should equal(result) - end - - it "should benchmark the compile process" do - Puppet::Node.stubs(:find).returns(@node) - @compiler.stubs(:networked?).returns(true) - @compiler.expects(:benchmark).with do |level, message| - level == :notice and message =~ /^Compiled catalog/ - end - Puppet::Parser::Compiler.stubs(:compile) - @compiler.find(@request) - end - end - - describe "when extracting facts from the request" do - before do - Facter.stubs(:value).returns "something" - @compiler = Puppet::Resource::Catalog::Compiler.new - @request = stub 'request', :options => {} - - @facts = stub 'facts', :save => nil - end - - it "should do nothing if no facts are provided" do - Puppet::Node::Facts.expects(:convert_from).never - @request.options[:facts] = nil - - @compiler.extract_facts_from_request(@request) - end - - it "should use the Facts class to deserialize the provided facts" do - @request.options[:facts_format] = "foo" - @request.options[:facts] = "bar" - Puppet::Node::Facts.expects(:convert_from).returns @facts - - @compiler.extract_facts_from_request(@request) - end - - it "should use the provided fact format" do - @request.options[:facts_format] = "foo" - @request.options[:facts] = "bar" - Puppet::Node::Facts.expects(:convert_from).with { |format, text| format == "foo" }.returns @facts - - @compiler.extract_facts_from_request(@request) - end - - it "should convert the facts into a fact instance and save it" do - @request.options[:facts_format] = "foo" - @request.options[:facts] = "bar" - Puppet::Node::Facts.expects(:convert_from).returns @facts - - @facts.expects(:save) - - @compiler.extract_facts_from_request(@request) - end - end - - describe "when finding nodes" do - before do - Facter.stubs(:value).returns("whatever") - @compiler = Puppet::Resource::Catalog::Compiler.new - @name = "me" - @node = mock 'node' - @request = stub 'request', :node => @name, :options => {} - @compiler.stubs(:compile) - end - - it "should look node information up via the Node class with the provided key" do - @node.stubs :merge - Puppet::Node.expects(:find).with(@name).returns(@node) - @compiler.find(@request) - end - end - - describe "after finding nodes" do - before do - Puppet.expects(:version).returns(1) - Facter.expects(:value).with('fqdn').returns("my.server.com") - Facter.expects(:value).with('ipaddress').returns("my.ip.address") - @compiler = Puppet::Resource::Catalog::Compiler.new - @name = "me" - @node = mock 'node' - @request = stub 'request', :node => @name, :options => {} - @compiler.stubs(:compile) - Puppet::Node.stubs(:find).with(@name).returns(@node) - end - - it "should add the server's Puppet version to the node's parameters as 'serverversion'" do - @node.expects(:merge).with { |args| args["serverversion"] == "1" } - @compiler.find(@request) - end - - it "should add the server's fqdn to the node's parameters as 'servername'" do - @node.expects(:merge).with { |args| args["servername"] == "my.server.com" } - @compiler.find(@request) - end - - it "should add the server's IP address to the node's parameters as 'serverip'" do - @node.expects(:merge).with { |args| args["serverip"] == "my.ip.address" } - @compiler.find(@request) - end - end - - describe "when filtering resources" do - before :each do - Facter.stubs(:value) - @compiler = Puppet::Resource::Catalog::Compiler.new - @catalog = stub_everything 'catalog' - @catalog.stubs(:respond_to?).with(:filter).returns(true) - end - - it "should delegate to the catalog instance filtering" do - @catalog.expects(:filter) - @compiler.filter(@catalog) - end - - it "should filter out virtual resources" do - resource = mock 'resource', :virtual? => true - @catalog.stubs(:filter).yields(resource) - - @compiler.filter(@catalog) - end - - it "should return the same catalog if it doesn't support filtering" do - @catalog.stubs(:respond_to?).with(:filter).returns(false) - - @compiler.filter(@catalog).should == @catalog - end - - it "should return the filtered catalog" do - catalog = stub 'filtered catalog' - @catalog.stubs(:filter).returns(catalog) - - @compiler.filter(@catalog).should == catalog - end - - end -end diff --git a/spec/unit/indirector/catalog/queue_spec.rb b/spec/unit/indirector/catalog/queue_spec.rb new file mode 100755 index 000000000..66a30c0d6 --- /dev/null +++ b/spec/unit/indirector/catalog/queue_spec.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/catalog/queue' + +describe Puppet::Resource::Catalog::Queue do + it 'should be a subclass of the Queue terminus' do + Puppet::Resource::Catalog::Queue.superclass.should equal(Puppet::Indirector::Queue) + end + + it 'should be registered with the catalog store indirection' do + indirection = Puppet::Indirector::Indirection.instance(:catalog) + Puppet::Resource::Catalog::Queue.indirection.should equal(indirection) + end + + it 'shall be dubbed ":queue"' do + Puppet::Resource::Catalog::Queue.name.should == :queue + end +end diff --git a/spec/unit/indirector/catalog/queue_spec_spec.rb b/spec/unit/indirector/catalog/queue_spec_spec.rb deleted file mode 100755 index 66a30c0d6..000000000 --- a/spec/unit/indirector/catalog/queue_spec_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/catalog/queue' - -describe Puppet::Resource::Catalog::Queue do - it 'should be a subclass of the Queue terminus' do - Puppet::Resource::Catalog::Queue.superclass.should equal(Puppet::Indirector::Queue) - end - - it 'should be registered with the catalog store indirection' do - indirection = Puppet::Indirector::Indirection.instance(:catalog) - Puppet::Resource::Catalog::Queue.indirection.should equal(indirection) - end - - it 'shall be dubbed ":queue"' do - Puppet::Resource::Catalog::Queue.name.should == :queue - end -end diff --git a/spec/unit/indirector/catalog/rest_spec.rb b/spec/unit/indirector/catalog/rest_spec.rb new file mode 100755 index 000000000..909e0a89e --- /dev/null +++ b/spec/unit/indirector/catalog/rest_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/catalog/rest' + +describe Puppet::Resource::Catalog::Rest do + it "should be a sublcass of Puppet::Indirector::REST" do + Puppet::Resource::Catalog::Rest.superclass.should equal(Puppet::Indirector::REST) + end +end diff --git a/spec/unit/indirector/catalog/rest_spec_spec.rb b/spec/unit/indirector/catalog/rest_spec_spec.rb deleted file mode 100755 index 909e0a89e..000000000 --- a/spec/unit/indirector/catalog/rest_spec_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/catalog/rest' - -describe Puppet::Resource::Catalog::Rest do - it "should be a sublcass of Puppet::Indirector::REST" do - Puppet::Resource::Catalog::Rest.superclass.should equal(Puppet::Indirector::REST) - end -end diff --git a/spec/unit/indirector/catalog/yaml_spec.rb b/spec/unit/indirector/catalog/yaml_spec.rb new file mode 100755 index 000000000..5f233c91c --- /dev/null +++ b/spec/unit/indirector/catalog/yaml_spec.rb @@ -0,0 +1,25 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/resource/catalog' +require 'puppet/indirector/catalog/yaml' + +describe Puppet::Resource::Catalog::Yaml do + it "should be a subclass of the Yaml terminus" do + Puppet::Resource::Catalog::Yaml.superclass.should equal(Puppet::Indirector::Yaml) + end + + it "should have documentation" do + Puppet::Resource::Catalog::Yaml.doc.should_not be_nil + end + + it "should be registered with the catalog store indirection" do + indirection = Puppet::Indirector::Indirection.instance(:catalog) + Puppet::Resource::Catalog::Yaml.indirection.should equal(indirection) + end + + it "should have its name set to :yaml" do + Puppet::Resource::Catalog::Yaml.name.should == :yaml + end +end diff --git a/spec/unit/indirector/catalog/yaml_spec_spec.rb b/spec/unit/indirector/catalog/yaml_spec_spec.rb deleted file mode 100755 index 5f233c91c..000000000 --- a/spec/unit/indirector/catalog/yaml_spec_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/resource/catalog' -require 'puppet/indirector/catalog/yaml' - -describe Puppet::Resource::Catalog::Yaml do - it "should be a subclass of the Yaml terminus" do - Puppet::Resource::Catalog::Yaml.superclass.should equal(Puppet::Indirector::Yaml) - end - - it "should have documentation" do - Puppet::Resource::Catalog::Yaml.doc.should_not be_nil - end - - it "should be registered with the catalog store indirection" do - indirection = Puppet::Indirector::Indirection.instance(:catalog) - Puppet::Resource::Catalog::Yaml.indirection.should equal(indirection) - end - - it "should have its name set to :yaml" do - Puppet::Resource::Catalog::Yaml.name.should == :yaml - end -end diff --git a/spec/unit/indirector/certificate/ca_spec.rb b/spec/unit/indirector/certificate/ca_spec.rb new file mode 100755 index 000000000..0158a8d90 --- /dev/null +++ b/spec/unit/indirector/certificate/ca_spec.rb @@ -0,0 +1,28 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-7. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/certificate/ca' + +describe Puppet::SSL::Certificate::Ca do + it "should have documentation" do + Puppet::SSL::Certificate::Ca.doc.should be_instance_of(String) + end + + it "should use the :signeddir as the collection directory" do + Puppet.settings.expects(:value).with(:signeddir).returns "/cert/dir" + Puppet::SSL::Certificate::Ca.collection_directory.should == "/cert/dir" + end + + it "should store the ca certificate at the :cacert location" do + Puppet.settings.stubs(:use) + Puppet.settings.stubs(:value).returns "whatever" + Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" + file = Puppet::SSL::Certificate::Ca.new + file.stubs(:ca?).returns true + file.path("whatever").should == "/ca/cert" + end +end diff --git a/spec/unit/indirector/certificate/ca_spec_spec.rb b/spec/unit/indirector/certificate/ca_spec_spec.rb deleted file mode 100755 index 0158a8d90..000000000 --- a/spec/unit/indirector/certificate/ca_spec_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-7. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/certificate/ca' - -describe Puppet::SSL::Certificate::Ca do - it "should have documentation" do - Puppet::SSL::Certificate::Ca.doc.should be_instance_of(String) - end - - it "should use the :signeddir as the collection directory" do - Puppet.settings.expects(:value).with(:signeddir).returns "/cert/dir" - Puppet::SSL::Certificate::Ca.collection_directory.should == "/cert/dir" - end - - it "should store the ca certificate at the :cacert location" do - Puppet.settings.stubs(:use) - Puppet.settings.stubs(:value).returns "whatever" - Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" - file = Puppet::SSL::Certificate::Ca.new - file.stubs(:ca?).returns true - file.path("whatever").should == "/ca/cert" - end -end diff --git a/spec/unit/indirector/certificate/file_spec.rb b/spec/unit/indirector/certificate/file_spec.rb new file mode 100755 index 000000000..6fb00f1ca --- /dev/null +++ b/spec/unit/indirector/certificate/file_spec.rb @@ -0,0 +1,28 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-7. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/certificate/file' + +describe Puppet::SSL::Certificate::File do + it "should have documentation" do + Puppet::SSL::Certificate::File.doc.should be_instance_of(String) + end + + it "should use the :certdir as the collection directory" do + Puppet.settings.expects(:value).with(:certdir).returns "/cert/dir" + Puppet::SSL::Certificate::File.collection_directory.should == "/cert/dir" + end + + it "should store the ca certificate at the :localcacert location" do + Puppet.settings.stubs(:use) + Puppet.settings.stubs(:value).returns "whatever" + Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert" + file = Puppet::SSL::Certificate::File.new + file.stubs(:ca?).returns true + file.path("whatever").should == "/ca/cert" + end +end diff --git a/spec/unit/indirector/certificate/file_spec_spec.rb b/spec/unit/indirector/certificate/file_spec_spec.rb deleted file mode 100755 index 6fb00f1ca..000000000 --- a/spec/unit/indirector/certificate/file_spec_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-7. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/certificate/file' - -describe Puppet::SSL::Certificate::File do - it "should have documentation" do - Puppet::SSL::Certificate::File.doc.should be_instance_of(String) - end - - it "should use the :certdir as the collection directory" do - Puppet.settings.expects(:value).with(:certdir).returns "/cert/dir" - Puppet::SSL::Certificate::File.collection_directory.should == "/cert/dir" - end - - it "should store the ca certificate at the :localcacert location" do - Puppet.settings.stubs(:use) - Puppet.settings.stubs(:value).returns "whatever" - Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert" - file = Puppet::SSL::Certificate::File.new - file.stubs(:ca?).returns true - file.path("whatever").should == "/ca/cert" - end -end diff --git a/spec/unit/indirector/certificate/rest_spec.rb b/spec/unit/indirector/certificate/rest_spec.rb new file mode 100755 index 000000000..9f272fbb2 --- /dev/null +++ b/spec/unit/indirector/certificate/rest_spec.rb @@ -0,0 +1,58 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/certificate/rest' + +describe Puppet::SSL::Certificate::Rest do + before do + @searcher = Puppet::SSL::Certificate::Rest.new + end + + it "should be a sublcass of Puppet::Indirector::REST" do + Puppet::SSL::Certificate::Rest.superclass.should equal(Puppet::Indirector::REST) + end + + it "should set server_setting to :ca_server" do + Puppet::SSL::Certificate::Rest.server_setting.should == :ca_server + end + + it "should set port_setting to :ca_port" do + Puppet::SSL::Certificate::Rest.port_setting.should == :ca_port + end + + it "should make sure found certificates have their names set to the search string" do + terminus = Puppet::SSL::Certificate::Rest.new + + # This has 'boo.com' in the CN + cert_string = "-----BEGIN CERTIFICATE----- +MIICPzCCAaigAwIBAgIBBDANBgkqhkiG9w0BAQUFADAWMRQwEgYDVQQDDAtidWNr +eS5sb2NhbDAeFw0wOTA5MTcxNzI1MzJaFw0xNDA5MTYxNzI1MzJaMBIxEDAOBgNV +BAMMB2Jvby5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKG9B+DkTCNh +F5xHchNDfnbC9NzWKM600oxrr84pgUVAG6B2wAZcdfoEtXszhsY9Jzpwqkvxk4Mx +AbYqo9+TCi4UoiH6e+vAKOOJD3DHrlf+/RW4hGtyaI41DBhf4+B4/oFz5PH9mvKe +NSfHFI/yPW+1IXYjxKLQNwF9E7q3JbnzAgMBAAGjgaAwgZ0wOAYJYIZIAYb4QgEN +BCsWKVB1cHBldCBSdWJ5L09wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMAwG +A1UdEwEB/wQCMAAwHQYDVR0OBBYEFJOxEUeyf4cNOBmf9zIaE1JTuNdLMAsGA1Ud +DwQEAwIFoDAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwME +MA0GCSqGSIb3DQEBBQUAA4GBAFTJxKprMg6tfhGnvEvURPmlJrINn9c2b5Y4AGYp +tO86PFFkWw/EIJvvJzbj3s+Butr+eUo//+f1xxX7UCwwGqGxKqjtVS219oU/wkx8 +h7rW4Xk7MrLl0auSS1p4wLcAMm+ZImf94+j8Cj+tkr8eGozZceRV13b8+EkdaE3S +rn/G +-----END CERTIFICATE----- +" + + network = stub 'network' + terminus.stubs(:network).returns network + + response = stub 'response', :code => "200", :body => cert_string + response.stubs(:[]).with('content-type').returns "text/plain" + response.stubs(:[]).with('content-encoding') + network.expects(:get).returns response + + request = Puppet::Indirector::Request.new(:certificate, :find, "foo.com") + result = terminus.find(request) + result.should_not be_nil + result.name.should == "foo.com" + end +end diff --git a/spec/unit/indirector/certificate/rest_spec_spec.rb b/spec/unit/indirector/certificate/rest_spec_spec.rb deleted file mode 100755 index 9f272fbb2..000000000 --- a/spec/unit/indirector/certificate/rest_spec_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/certificate/rest' - -describe Puppet::SSL::Certificate::Rest do - before do - @searcher = Puppet::SSL::Certificate::Rest.new - end - - it "should be a sublcass of Puppet::Indirector::REST" do - Puppet::SSL::Certificate::Rest.superclass.should equal(Puppet::Indirector::REST) - end - - it "should set server_setting to :ca_server" do - Puppet::SSL::Certificate::Rest.server_setting.should == :ca_server - end - - it "should set port_setting to :ca_port" do - Puppet::SSL::Certificate::Rest.port_setting.should == :ca_port - end - - it "should make sure found certificates have their names set to the search string" do - terminus = Puppet::SSL::Certificate::Rest.new - - # This has 'boo.com' in the CN - cert_string = "-----BEGIN CERTIFICATE----- -MIICPzCCAaigAwIBAgIBBDANBgkqhkiG9w0BAQUFADAWMRQwEgYDVQQDDAtidWNr -eS5sb2NhbDAeFw0wOTA5MTcxNzI1MzJaFw0xNDA5MTYxNzI1MzJaMBIxEDAOBgNV -BAMMB2Jvby5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKG9B+DkTCNh -F5xHchNDfnbC9NzWKM600oxrr84pgUVAG6B2wAZcdfoEtXszhsY9Jzpwqkvxk4Mx -AbYqo9+TCi4UoiH6e+vAKOOJD3DHrlf+/RW4hGtyaI41DBhf4+B4/oFz5PH9mvKe -NSfHFI/yPW+1IXYjxKLQNwF9E7q3JbnzAgMBAAGjgaAwgZ0wOAYJYIZIAYb4QgEN -BCsWKVB1cHBldCBSdWJ5L09wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMAwG -A1UdEwEB/wQCMAAwHQYDVR0OBBYEFJOxEUeyf4cNOBmf9zIaE1JTuNdLMAsGA1Ud -DwQEAwIFoDAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwME -MA0GCSqGSIb3DQEBBQUAA4GBAFTJxKprMg6tfhGnvEvURPmlJrINn9c2b5Y4AGYp -tO86PFFkWw/EIJvvJzbj3s+Butr+eUo//+f1xxX7UCwwGqGxKqjtVS219oU/wkx8 -h7rW4Xk7MrLl0auSS1p4wLcAMm+ZImf94+j8Cj+tkr8eGozZceRV13b8+EkdaE3S -rn/G ------END CERTIFICATE----- -" - - network = stub 'network' - terminus.stubs(:network).returns network - - response = stub 'response', :code => "200", :body => cert_string - response.stubs(:[]).with('content-type').returns "text/plain" - response.stubs(:[]).with('content-encoding') - network.expects(:get).returns response - - request = Puppet::Indirector::Request.new(:certificate, :find, "foo.com") - result = terminus.find(request) - result.should_not be_nil - result.name.should == "foo.com" - end -end diff --git a/spec/unit/indirector/certificate_request/ca_spec.rb b/spec/unit/indirector/certificate_request/ca_spec.rb new file mode 100755 index 000000000..b35d8b474 --- /dev/null +++ b/spec/unit/indirector/certificate_request/ca_spec.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-7. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/certificate_request/ca' + +describe Puppet::SSL::CertificateRequest::Ca do + it "should have documentation" do + Puppet::SSL::CertificateRequest::Ca.doc.should be_instance_of(String) + end + + it "should use the :csrdir as the collection directory" do + Puppet.settings.expects(:value).with(:csrdir).returns "/request/dir" + Puppet::SSL::CertificateRequest::Ca.collection_directory.should == "/request/dir" + end +end diff --git a/spec/unit/indirector/certificate_request/ca_spec_spec.rb b/spec/unit/indirector/certificate_request/ca_spec_spec.rb deleted file mode 100755 index b35d8b474..000000000 --- a/spec/unit/indirector/certificate_request/ca_spec_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-7. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/certificate_request/ca' - -describe Puppet::SSL::CertificateRequest::Ca do - it "should have documentation" do - Puppet::SSL::CertificateRequest::Ca.doc.should be_instance_of(String) - end - - it "should use the :csrdir as the collection directory" do - Puppet.settings.expects(:value).with(:csrdir).returns "/request/dir" - Puppet::SSL::CertificateRequest::Ca.collection_directory.should == "/request/dir" - end -end diff --git a/spec/unit/indirector/certificate_request/file_spec.rb b/spec/unit/indirector/certificate_request/file_spec.rb new file mode 100755 index 000000000..e1f442e2a --- /dev/null +++ b/spec/unit/indirector/certificate_request/file_spec.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-7. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/certificate_request/file' + +describe Puppet::SSL::CertificateRequest::File do + it "should have documentation" do + Puppet::SSL::CertificateRequest::File.doc.should be_instance_of(String) + end + + it "should use the :requestdir as the collection directory" do + Puppet.settings.expects(:value).with(:requestdir).returns "/request/dir" + Puppet::SSL::CertificateRequest::File.collection_directory.should == "/request/dir" + end +end diff --git a/spec/unit/indirector/certificate_request/file_spec_spec.rb b/spec/unit/indirector/certificate_request/file_spec_spec.rb deleted file mode 100755 index e1f442e2a..000000000 --- a/spec/unit/indirector/certificate_request/file_spec_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-7. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/certificate_request/file' - -describe Puppet::SSL::CertificateRequest::File do - it "should have documentation" do - Puppet::SSL::CertificateRequest::File.doc.should be_instance_of(String) - end - - it "should use the :requestdir as the collection directory" do - Puppet.settings.expects(:value).with(:requestdir).returns "/request/dir" - Puppet::SSL::CertificateRequest::File.collection_directory.should == "/request/dir" - end -end diff --git a/spec/unit/indirector/certificate_request/rest_spec.rb b/spec/unit/indirector/certificate_request/rest_spec.rb new file mode 100755 index 000000000..a1492acab --- /dev/null +++ b/spec/unit/indirector/certificate_request/rest_spec.rb @@ -0,0 +1,23 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/certificate_request/rest' + +describe Puppet::SSL::CertificateRequest::Rest do + before do + @searcher = Puppet::SSL::CertificateRequest::Rest.new + end + + it "should be a sublcass of Puppet::Indirector::REST" do + Puppet::SSL::CertificateRequest::Rest.superclass.should equal(Puppet::Indirector::REST) + end + + it "should set server_setting to :ca_server" do + Puppet::SSL::CertificateRequest::Rest.server_setting.should == :ca_server + end + + it "should set port_setting to :ca_port" do + Puppet::SSL::CertificateRequest::Rest.port_setting.should == :ca_port + end +end diff --git a/spec/unit/indirector/certificate_request/rest_spec_spec.rb b/spec/unit/indirector/certificate_request/rest_spec_spec.rb deleted file mode 100755 index a1492acab..000000000 --- a/spec/unit/indirector/certificate_request/rest_spec_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/certificate_request/rest' - -describe Puppet::SSL::CertificateRequest::Rest do - before do - @searcher = Puppet::SSL::CertificateRequest::Rest.new - end - - it "should be a sublcass of Puppet::Indirector::REST" do - Puppet::SSL::CertificateRequest::Rest.superclass.should equal(Puppet::Indirector::REST) - end - - it "should set server_setting to :ca_server" do - Puppet::SSL::CertificateRequest::Rest.server_setting.should == :ca_server - end - - it "should set port_setting to :ca_port" do - Puppet::SSL::CertificateRequest::Rest.port_setting.should == :ca_port - end -end diff --git a/spec/unit/indirector/certificate_revocation_list/ca_spec.rb b/spec/unit/indirector/certificate_revocation_list/ca_spec.rb new file mode 100755 index 000000000..f6da86861 --- /dev/null +++ b/spec/unit/indirector/certificate_revocation_list/ca_spec.rb @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-7. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/certificate_revocation_list/ca' + +describe Puppet::SSL::CertificateRevocationList::Ca do + it "should have documentation" do + Puppet::SSL::CertificateRevocationList::Ca.doc.should be_instance_of(String) + end + + it "should use the :cacrl setting as the crl location" do + Puppet.settings.stubs(:value).returns "whatever" + Puppet.settings.stubs(:use) + Puppet.settings.stubs(:value).with(:cacrl).returns "/request/dir" + Puppet::SSL::CertificateRevocationList::Ca.new.path("whatever").should == "/request/dir" + end +end diff --git a/spec/unit/indirector/certificate_revocation_list/ca_spec_spec.rb b/spec/unit/indirector/certificate_revocation_list/ca_spec_spec.rb deleted file mode 100755 index f6da86861..000000000 --- a/spec/unit/indirector/certificate_revocation_list/ca_spec_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-7. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/certificate_revocation_list/ca' - -describe Puppet::SSL::CertificateRevocationList::Ca do - it "should have documentation" do - Puppet::SSL::CertificateRevocationList::Ca.doc.should be_instance_of(String) - end - - it "should use the :cacrl setting as the crl location" do - Puppet.settings.stubs(:value).returns "whatever" - Puppet.settings.stubs(:use) - Puppet.settings.stubs(:value).with(:cacrl).returns "/request/dir" - Puppet::SSL::CertificateRevocationList::Ca.new.path("whatever").should == "/request/dir" - end -end diff --git a/spec/unit/indirector/certificate_revocation_list/file_spec.rb b/spec/unit/indirector/certificate_revocation_list/file_spec.rb new file mode 100755 index 000000000..5db4f8c06 --- /dev/null +++ b/spec/unit/indirector/certificate_revocation_list/file_spec.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-7. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/certificate_revocation_list/file' + +describe Puppet::SSL::CertificateRevocationList::File do + it "should have documentation" do + Puppet::SSL::CertificateRevocationList::File.doc.should be_instance_of(String) + end + + it "should always store the file to :hostcrl location" do + Puppet.settings.expects(:value).with(:hostcrl).returns "/host/crl" + Puppet.settings.stubs(:use) + Puppet::SSL::CertificateRevocationList::File.file_location.should == "/host/crl" + end +end diff --git a/spec/unit/indirector/certificate_revocation_list/file_spec_spec.rb b/spec/unit/indirector/certificate_revocation_list/file_spec_spec.rb deleted file mode 100755 index 5db4f8c06..000000000 --- a/spec/unit/indirector/certificate_revocation_list/file_spec_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-7. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/certificate_revocation_list/file' - -describe Puppet::SSL::CertificateRevocationList::File do - it "should have documentation" do - Puppet::SSL::CertificateRevocationList::File.doc.should be_instance_of(String) - end - - it "should always store the file to :hostcrl location" do - Puppet.settings.expects(:value).with(:hostcrl).returns "/host/crl" - Puppet.settings.stubs(:use) - Puppet::SSL::CertificateRevocationList::File.file_location.should == "/host/crl" - end -end diff --git a/spec/unit/indirector/certificate_revocation_list/rest_spec.rb b/spec/unit/indirector/certificate_revocation_list/rest_spec.rb new file mode 100755 index 000000000..4c7cdc3d5 --- /dev/null +++ b/spec/unit/indirector/certificate_revocation_list/rest_spec.rb @@ -0,0 +1,23 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/certificate_revocation_list/rest' + +describe Puppet::SSL::CertificateRevocationList::Rest do + before do + @searcher = Puppet::SSL::CertificateRevocationList::Rest.new + end + + it "should be a sublcass of Puppet::Indirector::REST" do + Puppet::SSL::CertificateRevocationList::Rest.superclass.should equal(Puppet::Indirector::REST) + end + + it "should set server_setting to :ca_server" do + Puppet::SSL::CertificateRevocationList::Rest.server_setting.should == :ca_server + end + + it "should set port_setting to :ca_port" do + Puppet::SSL::CertificateRevocationList::Rest.port_setting.should == :ca_port + end +end diff --git a/spec/unit/indirector/certificate_revocation_list/rest_spec_spec.rb b/spec/unit/indirector/certificate_revocation_list/rest_spec_spec.rb deleted file mode 100755 index 4c7cdc3d5..000000000 --- a/spec/unit/indirector/certificate_revocation_list/rest_spec_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/certificate_revocation_list/rest' - -describe Puppet::SSL::CertificateRevocationList::Rest do - before do - @searcher = Puppet::SSL::CertificateRevocationList::Rest.new - end - - it "should be a sublcass of Puppet::Indirector::REST" do - Puppet::SSL::CertificateRevocationList::Rest.superclass.should equal(Puppet::Indirector::REST) - end - - it "should set server_setting to :ca_server" do - Puppet::SSL::CertificateRevocationList::Rest.server_setting.should == :ca_server - end - - it "should set port_setting to :ca_port" do - Puppet::SSL::CertificateRevocationList::Rest.port_setting.should == :ca_port - end -end diff --git a/spec/unit/indirector/code_spec.rb b/spec/unit/indirector/code_spec.rb new file mode 100755 index 000000000..3eb7540ef --- /dev/null +++ b/spec/unit/indirector/code_spec.rb @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/indirector/code' + +describe Puppet::Indirector::Code do + before do + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @model = mock 'model' + @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model + Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) + + @code_class = Class.new(Puppet::Indirector::Code) do + def self.to_s + "Mystuff::Testing" + end + end + + @searcher = @code_class.new + end + + it "should not have a find() method defined" do + @searcher.should_not respond_to(:find) + end + + it "should not have a save() method defined" do + @searcher.should_not respond_to(:save) + end + + it "should not have a destroy() method defined" do + @searcher.should_not respond_to(:destroy) + end +end diff --git a/spec/unit/indirector/code_spec_spec.rb b/spec/unit/indirector/code_spec_spec.rb deleted file mode 100755 index 3eb7540ef..000000000 --- a/spec/unit/indirector/code_spec_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector/code' - -describe Puppet::Indirector::Code do - before do - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @model = mock 'model' - @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model - Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - - @code_class = Class.new(Puppet::Indirector::Code) do - def self.to_s - "Mystuff::Testing" - end - end - - @searcher = @code_class.new - end - - it "should not have a find() method defined" do - @searcher.should_not respond_to(:find) - end - - it "should not have a save() method defined" do - @searcher.should_not respond_to(:save) - end - - it "should not have a destroy() method defined" do - @searcher.should_not respond_to(:destroy) - end -end diff --git a/spec/unit/indirector/direct_file_server_spec.rb b/spec/unit/indirector/direct_file_server_spec.rb new file mode 100755 index 000000000..e32fe6678 --- /dev/null +++ b/spec/unit/indirector/direct_file_server_spec.rb @@ -0,0 +1,84 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-10-24. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/indirector/direct_file_server' + +describe Puppet::Indirector::DirectFileServer do + before :each do + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @model = mock 'model' + @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model + Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) + + @direct_file_class = Class.new(Puppet::Indirector::DirectFileServer) do + def self.to_s + "Testing::Mytype" + end + end + + @server = @direct_file_class.new + + @uri = "file:///my/local" + + @request = Puppet::Indirector::Request.new(:mytype, :find, @uri) + end + + describe Puppet::Indirector::DirectFileServer, "when finding a single file" do + + it "should return nil if the file does not exist" do + FileTest.expects(:exists?).with("/my/local").returns false + @server.find(@request).should be_nil + end + + it "should return a Content instance created with the full path to the file if the file exists" do + FileTest.expects(:exists?).with("/my/local").returns true + @model.expects(:new).returns(:mycontent) + @server.find(@request).should == :mycontent + end + end + + describe Puppet::Indirector::DirectFileServer, "when creating the instance for a single found file" do + + before do + @data = mock 'content' + @data.stubs(:collect) + FileTest.expects(:exists?).with("/my/local").returns true + end + + it "should pass the full path to the instance" do + @model.expects(:new).with { |key, options| key == "/my/local" }.returns(@data) + @server.find(@request) + end + + it "should pass the :links setting on to the created Content instance if the file exists and there is a value for :links" do + @model.expects(:new).returns(@data) + @data.expects(:links=).with(:manage) + + @request.stubs(:options).returns(:links => :manage) + @server.find(@request) + end + end + + describe Puppet::Indirector::DirectFileServer, "when searching for multiple files" do + it "should return nil if the file does not exist" do + FileTest.expects(:exists?).with("/my/local").returns false + @server.find(@request).should be_nil + end + + it "should use :path2instances from the terminus_helper to return instances if the file exists" do + FileTest.expects(:exists?).with("/my/local").returns true + @server.expects(:path2instances) + @server.search(@request) + end + + it "should pass the original request to :path2instances" do + FileTest.expects(:exists?).with("/my/local").returns true + @server.expects(:path2instances).with(@request, "/my/local") + @server.search(@request) + end + end +end diff --git a/spec/unit/indirector/direct_file_server_spec_spec.rb b/spec/unit/indirector/direct_file_server_spec_spec.rb deleted file mode 100755 index e32fe6678..000000000 --- a/spec/unit/indirector/direct_file_server_spec_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-10-24. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/indirector/direct_file_server' - -describe Puppet::Indirector::DirectFileServer do - before :each do - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @model = mock 'model' - @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model - Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - - @direct_file_class = Class.new(Puppet::Indirector::DirectFileServer) do - def self.to_s - "Testing::Mytype" - end - end - - @server = @direct_file_class.new - - @uri = "file:///my/local" - - @request = Puppet::Indirector::Request.new(:mytype, :find, @uri) - end - - describe Puppet::Indirector::DirectFileServer, "when finding a single file" do - - it "should return nil if the file does not exist" do - FileTest.expects(:exists?).with("/my/local").returns false - @server.find(@request).should be_nil - end - - it "should return a Content instance created with the full path to the file if the file exists" do - FileTest.expects(:exists?).with("/my/local").returns true - @model.expects(:new).returns(:mycontent) - @server.find(@request).should == :mycontent - end - end - - describe Puppet::Indirector::DirectFileServer, "when creating the instance for a single found file" do - - before do - @data = mock 'content' - @data.stubs(:collect) - FileTest.expects(:exists?).with("/my/local").returns true - end - - it "should pass the full path to the instance" do - @model.expects(:new).with { |key, options| key == "/my/local" }.returns(@data) - @server.find(@request) - end - - it "should pass the :links setting on to the created Content instance if the file exists and there is a value for :links" do - @model.expects(:new).returns(@data) - @data.expects(:links=).with(:manage) - - @request.stubs(:options).returns(:links => :manage) - @server.find(@request) - end - end - - describe Puppet::Indirector::DirectFileServer, "when searching for multiple files" do - it "should return nil if the file does not exist" do - FileTest.expects(:exists?).with("/my/local").returns false - @server.find(@request).should be_nil - end - - it "should use :path2instances from the terminus_helper to return instances if the file exists" do - FileTest.expects(:exists?).with("/my/local").returns true - @server.expects(:path2instances) - @server.search(@request) - end - - it "should pass the original request to :path2instances" do - FileTest.expects(:exists?).with("/my/local").returns true - @server.expects(:path2instances).with(@request, "/my/local") - @server.search(@request) - end - end -end diff --git a/spec/unit/indirector/envelope_spec.rb b/spec/unit/indirector/envelope_spec.rb new file mode 100755 index 000000000..17c62023a --- /dev/null +++ b/spec/unit/indirector/envelope_spec.rb @@ -0,0 +1,47 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/indirector/envelope' + +describe Puppet::Indirector::Envelope do + before do + @instance = Object.new + @instance.extend(Puppet::Indirector::Envelope) + end + + it "should have an expiration accessor" do + @instance.expiration = "testing" + @instance.expiration.should == "testing" + end + + it "should have an expiration setter" do + @instance.should respond_to(:expiration=) + end + + it "should have a means of testing whether it is expired" do + @instance.should respond_to(:expired?) + end + + describe "when testing if it is expired" do + it "should return false if there is no expiration set" do + @instance.should_not be_expired + end + + it "should return true if the current date is after the expiration date" do + @instance.expiration = Time.now - 10 + @instance.should be_expired + end + + it "should return false if the current date is prior to the expiration date" do + @instance.expiration = Time.now + 10 + @instance.should_not be_expired + end + + it "should return false if the current date is equal to the expiration date" do + now = Time.now + Time.stubs(:now).returns(now) + @instance.expiration = now + @instance.should_not be_expired + end + end +end diff --git a/spec/unit/indirector/envelope_spec_spec.rb b/spec/unit/indirector/envelope_spec_spec.rb deleted file mode 100755 index 17c62023a..000000000 --- a/spec/unit/indirector/envelope_spec_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector/envelope' - -describe Puppet::Indirector::Envelope do - before do - @instance = Object.new - @instance.extend(Puppet::Indirector::Envelope) - end - - it "should have an expiration accessor" do - @instance.expiration = "testing" - @instance.expiration.should == "testing" - end - - it "should have an expiration setter" do - @instance.should respond_to(:expiration=) - end - - it "should have a means of testing whether it is expired" do - @instance.should respond_to(:expired?) - end - - describe "when testing if it is expired" do - it "should return false if there is no expiration set" do - @instance.should_not be_expired - end - - it "should return true if the current date is after the expiration date" do - @instance.expiration = Time.now - 10 - @instance.should be_expired - end - - it "should return false if the current date is prior to the expiration date" do - @instance.expiration = Time.now + 10 - @instance.should_not be_expired - end - - it "should return false if the current date is equal to the expiration date" do - now = Time.now - Time.stubs(:now).returns(now) - @instance.expiration = now - @instance.should_not be_expired - end - end -end diff --git a/spec/unit/indirector/exec_spec.rb b/spec/unit/indirector/exec_spec.rb new file mode 100755 index 000000000..4a7e2b50e --- /dev/null +++ b/spec/unit/indirector/exec_spec.rb @@ -0,0 +1,56 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/indirector/exec' + +describe Puppet::Indirector::Exec do + before do + @indirection = stub 'indirection', :name => :testing + Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection) + @exec_class = Class.new(Puppet::Indirector::Exec) do + def self.to_s + "Testing::Mytype" + end + + attr_accessor :command + end + + @searcher = @exec_class.new + @searcher.command = ["/echo"] + + @request = stub 'request', :key => "foo" + end + + it "should throw an exception if the command is not an array" do + @searcher.command = "/usr/bin/echo" + proc { @searcher.find(@request) }.should raise_error(Puppet::DevError) + end + + it "should throw an exception if the command is not fully qualified" do + @searcher.command = ["mycommand"] + proc { @searcher.find(@request) }.should raise_error(ArgumentError) + end + + it "should execute the command with the object name as the only argument" do + @searcher.expects(:execute).with(%w{/echo foo}) + @searcher.find(@request) + end + + it "should return the output of the script" do + @searcher.expects(:execute).with(%w{/echo foo}).returns("whatever") + @searcher.find(@request).should == "whatever" + end + + it "should return nil when the command produces no output" do + @searcher.expects(:execute).with(%w{/echo foo}).returns(nil) + @searcher.find(@request).should be_nil + end + + it "should return nil and log an error if there's an execution failure" do + @searcher.expects(:execute).with(%w{/echo foo}).raises(Puppet::ExecutionFailure.new("message")) + + Puppet.expects(:err) + @searcher.find(@request).should be_nil + end +end diff --git a/spec/unit/indirector/exec_spec_spec.rb b/spec/unit/indirector/exec_spec_spec.rb deleted file mode 100755 index 4a7e2b50e..000000000 --- a/spec/unit/indirector/exec_spec_spec.rb +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/indirector/exec' - -describe Puppet::Indirector::Exec do - before do - @indirection = stub 'indirection', :name => :testing - Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection) - @exec_class = Class.new(Puppet::Indirector::Exec) do - def self.to_s - "Testing::Mytype" - end - - attr_accessor :command - end - - @searcher = @exec_class.new - @searcher.command = ["/echo"] - - @request = stub 'request', :key => "foo" - end - - it "should throw an exception if the command is not an array" do - @searcher.command = "/usr/bin/echo" - proc { @searcher.find(@request) }.should raise_error(Puppet::DevError) - end - - it "should throw an exception if the command is not fully qualified" do - @searcher.command = ["mycommand"] - proc { @searcher.find(@request) }.should raise_error(ArgumentError) - end - - it "should execute the command with the object name as the only argument" do - @searcher.expects(:execute).with(%w{/echo foo}) - @searcher.find(@request) - end - - it "should return the output of the script" do - @searcher.expects(:execute).with(%w{/echo foo}).returns("whatever") - @searcher.find(@request).should == "whatever" - end - - it "should return nil when the command produces no output" do - @searcher.expects(:execute).with(%w{/echo foo}).returns(nil) - @searcher.find(@request).should be_nil - end - - it "should return nil and log an error if there's an execution failure" do - @searcher.expects(:execute).with(%w{/echo foo}).raises(Puppet::ExecutionFailure.new("message")) - - Puppet.expects(:err) - @searcher.find(@request).should be_nil - end -end diff --git a/spec/unit/indirector/facts/active_record_spec.rb b/spec/unit/indirector/facts/active_record_spec.rb new file mode 100755 index 000000000..1718311c5 --- /dev/null +++ b/spec/unit/indirector/facts/active_record_spec.rb @@ -0,0 +1,104 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/node/facts' + +describe "Puppet::Node::Facts::ActiveRecord" do + confine "Missing Rails" => Puppet.features.rails? + + before do + require 'puppet/indirector/facts/active_record' + Puppet.features.stubs(:rails?).returns true + Puppet::Rails.stubs(:init) + @terminus = Puppet::Node::Facts::ActiveRecord.new + end + + it "should be a subclass of the ActiveRecord terminus class" do + Puppet::Node::Facts::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) + end + + it "should use Puppet::Rails::Host as its ActiveRecord model" do + Puppet::Node::Facts::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) + end + + describe "when finding an instance" do + before do + @request = stub 'request', :key => "foo" + end + + it "should use the Hosts ActiveRecord class to find the host" do + Puppet::Rails::Host.expects(:find_by_name).with { |key, args| key == "foo" } + @terminus.find(@request) + end + + it "should include the fact names and values when finding the host" do + Puppet::Rails::Host.expects(:find_by_name).with { |key, args| args[:include] == {:fact_values => :fact_name} } + @terminus.find(@request) + end + + it "should return nil if no host instance can be found" do + Puppet::Rails::Host.expects(:find_by_name).returns nil + + @terminus.find(@request).should be_nil + end + + it "should convert the node's parameters into a Facts instance if a host instance is found" do + host = stub 'host', :name => "foo" + host.expects(:get_facts_hash).returns("one" => [mock("two_value", :value => "two")], "three" => [mock("three_value", :value => "four")]) + + Puppet::Rails::Host.expects(:find_by_name).returns host + + result = @terminus.find(@request) + + result.should be_instance_of(Puppet::Node::Facts) + result.name.should == "foo" + result.values.should == {"one" => "two", "three" => "four"} + end + + it "should convert all single-member arrays into non-arrays" do + host = stub 'host', :name => "foo" + host.expects(:get_facts_hash).returns("one" => [mock("two_value", :value => "two")]) + + Puppet::Rails::Host.expects(:find_by_name).returns host + + @terminus.find(@request).values["one"].should == "two" + end + end + + describe "when saving an instance" do + before do + @host = stub 'host', :name => "foo", :save => nil, :merge_facts => nil + Puppet::Rails::Host.stubs(:find_by_name).returns @host + @facts = Puppet::Node::Facts.new("foo", "one" => "two", "three" => "four") + @request = stub 'request', :key => "foo", :instance => @facts + end + + it "should find the Rails host with the same name" do + Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host + + @terminus.save(@request) + end + + it "should create a new Rails host if none can be found" do + Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil + + Puppet::Rails::Host.expects(:create).with(:name => "foo").returns @host + + @terminus.save(@request) + end + + it "should set the facts as facts on the Rails host instance" do + # There is other stuff added to the hash. + @host.expects(:merge_facts).with { |args| args["one"] == "two" and args["three"] == "four" } + + @terminus.save(@request) + end + + it "should save the Rails host instance" do + @host.expects(:save) + + @terminus.save(@request) + end + end +end diff --git a/spec/unit/indirector/facts/active_record_spec_spec.rb b/spec/unit/indirector/facts/active_record_spec_spec.rb deleted file mode 100755 index 1718311c5..000000000 --- a/spec/unit/indirector/facts/active_record_spec_spec.rb +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/node/facts' - -describe "Puppet::Node::Facts::ActiveRecord" do - confine "Missing Rails" => Puppet.features.rails? - - before do - require 'puppet/indirector/facts/active_record' - Puppet.features.stubs(:rails?).returns true - Puppet::Rails.stubs(:init) - @terminus = Puppet::Node::Facts::ActiveRecord.new - end - - it "should be a subclass of the ActiveRecord terminus class" do - Puppet::Node::Facts::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) - end - - it "should use Puppet::Rails::Host as its ActiveRecord model" do - Puppet::Node::Facts::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) - end - - describe "when finding an instance" do - before do - @request = stub 'request', :key => "foo" - end - - it "should use the Hosts ActiveRecord class to find the host" do - Puppet::Rails::Host.expects(:find_by_name).with { |key, args| key == "foo" } - @terminus.find(@request) - end - - it "should include the fact names and values when finding the host" do - Puppet::Rails::Host.expects(:find_by_name).with { |key, args| args[:include] == {:fact_values => :fact_name} } - @terminus.find(@request) - end - - it "should return nil if no host instance can be found" do - Puppet::Rails::Host.expects(:find_by_name).returns nil - - @terminus.find(@request).should be_nil - end - - it "should convert the node's parameters into a Facts instance if a host instance is found" do - host = stub 'host', :name => "foo" - host.expects(:get_facts_hash).returns("one" => [mock("two_value", :value => "two")], "three" => [mock("three_value", :value => "four")]) - - Puppet::Rails::Host.expects(:find_by_name).returns host - - result = @terminus.find(@request) - - result.should be_instance_of(Puppet::Node::Facts) - result.name.should == "foo" - result.values.should == {"one" => "two", "three" => "four"} - end - - it "should convert all single-member arrays into non-arrays" do - host = stub 'host', :name => "foo" - host.expects(:get_facts_hash).returns("one" => [mock("two_value", :value => "two")]) - - Puppet::Rails::Host.expects(:find_by_name).returns host - - @terminus.find(@request).values["one"].should == "two" - end - end - - describe "when saving an instance" do - before do - @host = stub 'host', :name => "foo", :save => nil, :merge_facts => nil - Puppet::Rails::Host.stubs(:find_by_name).returns @host - @facts = Puppet::Node::Facts.new("foo", "one" => "two", "three" => "four") - @request = stub 'request', :key => "foo", :instance => @facts - end - - it "should find the Rails host with the same name" do - Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host - - @terminus.save(@request) - end - - it "should create a new Rails host if none can be found" do - Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil - - Puppet::Rails::Host.expects(:create).with(:name => "foo").returns @host - - @terminus.save(@request) - end - - it "should set the facts as facts on the Rails host instance" do - # There is other stuff added to the hash. - @host.expects(:merge_facts).with { |args| args["one"] == "two" and args["three"] == "four" } - - @terminus.save(@request) - end - - it "should save the Rails host instance" do - @host.expects(:save) - - @terminus.save(@request) - end - end -end diff --git a/spec/unit/indirector/facts/couch_spec.rb b/spec/unit/indirector/facts/couch_spec.rb new file mode 100644 index 000000000..088401280 --- /dev/null +++ b/spec/unit/indirector/facts/couch_spec.rb @@ -0,0 +1,98 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/node/facts' + +describe "Puppet::Node::Facts::Couch" do + confine "couchrest gem is missing; cannot test couch terminus" => Puppet.features.couchdb? + require 'puppet/indirector/facts/couch' if Puppet.features.couchdb? + + before do + @mock_db = mock('couch db') + mock_document = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.values) + mock_document.stubs(:database).returns(@mock_db) + @mock_db.stubs(:get).with(fake_request.key).returns(mock_document) + Puppet::Node::Facts::Couch.stubs(:db).returns(@mock_db) + end + + subject { Puppet::Node::Facts::Couch } + + describe "#find" do + describe "when the node document exists" do + it "should find the request by key" do + @mock_db.expects(:get).with(fake_request.key).returns({'_id' => fake_request.key, 'facts' => fake_request.instance.values}) + subject.new.find(fake_request).should == fake_request.instance + end + end + + describe "when the node document does not exist" do + before do + @mock_db.expects(:get). + with(fake_request.key). + raises(RestClient::ResourceNotFound) + end + + it "should return nil" do + subject.new.find(fake_request).should be_nil + end + + it "should send Puppet a debug message" do + Puppet.expects(:debug).with("No couchdb document with id: test.local") + subject.new.find(fake_request).should be_nil + end + + end + end + + describe "#save" do + describe "with options" do + subject do + lambda { Puppet::Node::Facts::Couch.new.save(fake_request([1])) } + end + + it { should raise_error(ArgumentError, "PUT does not accept options") } + end + + it "should save the json to the CouchDB database" do + @mock_db.expects(:save_doc).at_least_once.returns({'ok' => true }) + subject.new.save(fake_request) + end + + describe "when the document exists" do + before do + @doc = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.instance.values) + @mock_db.expects(:get).with(fake_request.key).returns(@doc) + end + + it "saves the document" do + @doc.expects(:save) + subject.new.save(fake_request) + end + + end + + describe "when the document does not exist" do + before do + @mock_db.expects(:get). + with(fake_request.key). + raises(RestClient::ResourceNotFound) + end + + it "saves the document" do + @mock_db.expects(:save_doc) + subject.new.save(fake_request) + end + + end + + end + + def fake_request(options={}) + facts = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml', 'test.local.yaml')) + Struct.new(:instance, :key, :options).new(facts, facts.name, options) + end + private :fake_request + +end + diff --git a/spec/unit/indirector/facts/couch_spec_spec.rb b/spec/unit/indirector/facts/couch_spec_spec.rb deleted file mode 100644 index 088401280..000000000 --- a/spec/unit/indirector/facts/couch_spec_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/node/facts' - -describe "Puppet::Node::Facts::Couch" do - confine "couchrest gem is missing; cannot test couch terminus" => Puppet.features.couchdb? - require 'puppet/indirector/facts/couch' if Puppet.features.couchdb? - - before do - @mock_db = mock('couch db') - mock_document = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.values) - mock_document.stubs(:database).returns(@mock_db) - @mock_db.stubs(:get).with(fake_request.key).returns(mock_document) - Puppet::Node::Facts::Couch.stubs(:db).returns(@mock_db) - end - - subject { Puppet::Node::Facts::Couch } - - describe "#find" do - describe "when the node document exists" do - it "should find the request by key" do - @mock_db.expects(:get).with(fake_request.key).returns({'_id' => fake_request.key, 'facts' => fake_request.instance.values}) - subject.new.find(fake_request).should == fake_request.instance - end - end - - describe "when the node document does not exist" do - before do - @mock_db.expects(:get). - with(fake_request.key). - raises(RestClient::ResourceNotFound) - end - - it "should return nil" do - subject.new.find(fake_request).should be_nil - end - - it "should send Puppet a debug message" do - Puppet.expects(:debug).with("No couchdb document with id: test.local") - subject.new.find(fake_request).should be_nil - end - - end - end - - describe "#save" do - describe "with options" do - subject do - lambda { Puppet::Node::Facts::Couch.new.save(fake_request([1])) } - end - - it { should raise_error(ArgumentError, "PUT does not accept options") } - end - - it "should save the json to the CouchDB database" do - @mock_db.expects(:save_doc).at_least_once.returns({'ok' => true }) - subject.new.save(fake_request) - end - - describe "when the document exists" do - before do - @doc = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.instance.values) - @mock_db.expects(:get).with(fake_request.key).returns(@doc) - end - - it "saves the document" do - @doc.expects(:save) - subject.new.save(fake_request) - end - - end - - describe "when the document does not exist" do - before do - @mock_db.expects(:get). - with(fake_request.key). - raises(RestClient::ResourceNotFound) - end - - it "saves the document" do - @mock_db.expects(:save_doc) - subject.new.save(fake_request) - end - - end - - end - - def fake_request(options={}) - facts = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml', 'test.local.yaml')) - Struct.new(:instance, :key, :options).new(facts, facts.name, options) - end - private :fake_request - -end - diff --git a/spec/unit/indirector/facts/facter_spec.rb b/spec/unit/indirector/facts/facter_spec.rb new file mode 100755 index 000000000..ea68f63d6 --- /dev/null +++ b/spec/unit/indirector/facts/facter_spec.rb @@ -0,0 +1,142 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-9-23. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/facts/facter' + +describe Puppet::Node::Facts::Facter do + it "should be a subclass of the Code terminus" do + Puppet::Node::Facts::Facter.superclass.should equal(Puppet::Indirector::Code) + end + + it "should have documentation" do + Puppet::Node::Facts::Facter.doc.should_not be_nil + end + + it "should be registered with the configuration store indirection" do + indirection = Puppet::Indirector::Indirection.instance(:facts) + Puppet::Node::Facts::Facter.indirection.should equal(indirection) + end + + it "should have its name set to :facter" do + Puppet::Node::Facts::Facter.name.should == :facter + end + + it "should load facts on initialization" do + Puppet::Node::Facts::Facter.expects(:load_fact_plugins) + Puppet::Node::Facts::Facter.new + end +end + +describe Puppet::Node::Facts::Facter do + before :each do + @facter = Puppet::Node::Facts::Facter.new + Facter.stubs(:to_hash).returns({}) + @name = "me" + @request = stub 'request', :key => @name + end + + describe Puppet::Node::Facts::Facter, " when finding facts" do + it "should return a Facts instance" do + @facter.find(@request).should be_instance_of(Puppet::Node::Facts) + end + + it "should return a Facts instance with the provided key as the name" do + @facter.find(@request).name.should == @name + end + + it "should return the Facter facts as the values in the Facts instance" do + Facter.expects(:to_hash).returns("one" => "two") + facts = @facter.find(@request) + facts.values["one"].should == "two" + end + + it "should add local facts" do + facts = Puppet::Node::Facts.new("foo") + Puppet::Node::Facts.expects(:new).returns facts + facts.expects(:add_local_facts) + + @facter.find(@request) + end + + it "should convert all facts into strings" do + facts = Puppet::Node::Facts.new("foo") + Puppet::Node::Facts.expects(:new).returns facts + facts.expects(:stringify) + + @facter.find(@request) + end + + it "should call the downcase hook" do + facts = Puppet::Node::Facts.new("foo") + Puppet::Node::Facts.expects(:new).returns facts + facts.expects(:downcase_if_necessary) + + @facter.find(@request) + end + end + + describe Puppet::Node::Facts::Facter, " when saving facts" do + + it "should fail" do + proc { @facter.save(@facts) }.should raise_error(Puppet::DevError) + end + end + + describe Puppet::Node::Facts::Facter, " when destroying facts" do + + it "should fail" do + proc { @facter.destroy(@facts) }.should raise_error(Puppet::DevError) + end + end + + it "should skip files when asked to load a directory" do + FileTest.expects(:directory?).with("myfile").returns false + + Puppet::Node::Facts::Facter.load_facts_in_dir("myfile") + end + + it "should load each ruby file when asked to load a directory" do + FileTest.expects(:directory?).with("mydir").returns true + Dir.expects(:chdir).with("mydir").yields + + Dir.expects(:glob).with("*.rb").returns %w{a.rb b.rb} + + Puppet::Node::Facts::Facter.expects(:load).with("a.rb") + Puppet::Node::Facts::Facter.expects(:load).with("b.rb") + + Puppet::Node::Facts::Facter.load_facts_in_dir("mydir") + end + + describe Puppet::Node::Facts::Facter, "when loading fact plugins from disk" do + it "should load each directory in the Fact path" do + Puppet.settings.stubs(:value).returns "foo" + Puppet.settings.expects(:value).with(:factpath).returns("one%stwo" % File::PATH_SEPARATOR) + + Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("one") + Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("two") + + Puppet::Node::Facts::Facter.load_fact_plugins + end + + it "should load all facts from the modules" do + Puppet.settings.stubs(:value).returns "foo" + Puppet::Node::Facts::Facter.stubs(:load_facts_in_dir) + + Puppet.settings.expects(:value).with(:modulepath).returns("one%stwo" % File::PATH_SEPARATOR) + + Dir.expects(:glob).with("one/*/plugins/facter").returns %w{oneA oneB} + Dir.expects(:glob).with("two/*/plugins/facter").returns %w{twoA twoB} + + Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneA") + Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneB") + Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoA") + Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoB") + + Puppet::Node::Facts::Facter.load_fact_plugins + end + end +end diff --git a/spec/unit/indirector/facts/facter_spec_spec.rb b/spec/unit/indirector/facts/facter_spec_spec.rb deleted file mode 100755 index ea68f63d6..000000000 --- a/spec/unit/indirector/facts/facter_spec_spec.rb +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-9-23. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/facts/facter' - -describe Puppet::Node::Facts::Facter do - it "should be a subclass of the Code terminus" do - Puppet::Node::Facts::Facter.superclass.should equal(Puppet::Indirector::Code) - end - - it "should have documentation" do - Puppet::Node::Facts::Facter.doc.should_not be_nil - end - - it "should be registered with the configuration store indirection" do - indirection = Puppet::Indirector::Indirection.instance(:facts) - Puppet::Node::Facts::Facter.indirection.should equal(indirection) - end - - it "should have its name set to :facter" do - Puppet::Node::Facts::Facter.name.should == :facter - end - - it "should load facts on initialization" do - Puppet::Node::Facts::Facter.expects(:load_fact_plugins) - Puppet::Node::Facts::Facter.new - end -end - -describe Puppet::Node::Facts::Facter do - before :each do - @facter = Puppet::Node::Facts::Facter.new - Facter.stubs(:to_hash).returns({}) - @name = "me" - @request = stub 'request', :key => @name - end - - describe Puppet::Node::Facts::Facter, " when finding facts" do - it "should return a Facts instance" do - @facter.find(@request).should be_instance_of(Puppet::Node::Facts) - end - - it "should return a Facts instance with the provided key as the name" do - @facter.find(@request).name.should == @name - end - - it "should return the Facter facts as the values in the Facts instance" do - Facter.expects(:to_hash).returns("one" => "two") - facts = @facter.find(@request) - facts.values["one"].should == "two" - end - - it "should add local facts" do - facts = Puppet::Node::Facts.new("foo") - Puppet::Node::Facts.expects(:new).returns facts - facts.expects(:add_local_facts) - - @facter.find(@request) - end - - it "should convert all facts into strings" do - facts = Puppet::Node::Facts.new("foo") - Puppet::Node::Facts.expects(:new).returns facts - facts.expects(:stringify) - - @facter.find(@request) - end - - it "should call the downcase hook" do - facts = Puppet::Node::Facts.new("foo") - Puppet::Node::Facts.expects(:new).returns facts - facts.expects(:downcase_if_necessary) - - @facter.find(@request) - end - end - - describe Puppet::Node::Facts::Facter, " when saving facts" do - - it "should fail" do - proc { @facter.save(@facts) }.should raise_error(Puppet::DevError) - end - end - - describe Puppet::Node::Facts::Facter, " when destroying facts" do - - it "should fail" do - proc { @facter.destroy(@facts) }.should raise_error(Puppet::DevError) - end - end - - it "should skip files when asked to load a directory" do - FileTest.expects(:directory?).with("myfile").returns false - - Puppet::Node::Facts::Facter.load_facts_in_dir("myfile") - end - - it "should load each ruby file when asked to load a directory" do - FileTest.expects(:directory?).with("mydir").returns true - Dir.expects(:chdir).with("mydir").yields - - Dir.expects(:glob).with("*.rb").returns %w{a.rb b.rb} - - Puppet::Node::Facts::Facter.expects(:load).with("a.rb") - Puppet::Node::Facts::Facter.expects(:load).with("b.rb") - - Puppet::Node::Facts::Facter.load_facts_in_dir("mydir") - end - - describe Puppet::Node::Facts::Facter, "when loading fact plugins from disk" do - it "should load each directory in the Fact path" do - Puppet.settings.stubs(:value).returns "foo" - Puppet.settings.expects(:value).with(:factpath).returns("one%stwo" % File::PATH_SEPARATOR) - - Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("one") - Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("two") - - Puppet::Node::Facts::Facter.load_fact_plugins - end - - it "should load all facts from the modules" do - Puppet.settings.stubs(:value).returns "foo" - Puppet::Node::Facts::Facter.stubs(:load_facts_in_dir) - - Puppet.settings.expects(:value).with(:modulepath).returns("one%stwo" % File::PATH_SEPARATOR) - - Dir.expects(:glob).with("one/*/plugins/facter").returns %w{oneA oneB} - Dir.expects(:glob).with("two/*/plugins/facter").returns %w{twoA twoB} - - Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneA") - Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneB") - Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoA") - Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoB") - - Puppet::Node::Facts::Facter.load_fact_plugins - end - end -end diff --git a/spec/unit/indirector/facts/rest_spec.rb b/spec/unit/indirector/facts/rest_spec.rb new file mode 100755 index 000000000..db46db02c --- /dev/null +++ b/spec/unit/indirector/facts/rest_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/facts/rest' + +describe Puppet::Node::Facts::Rest do + it "should be a sublcass of Puppet::Indirector::REST" do + Puppet::Node::Facts::Rest.superclass.should equal(Puppet::Indirector::REST) + end +end diff --git a/spec/unit/indirector/facts/rest_spec_spec.rb b/spec/unit/indirector/facts/rest_spec_spec.rb deleted file mode 100755 index db46db02c..000000000 --- a/spec/unit/indirector/facts/rest_spec_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/facts/rest' - -describe Puppet::Node::Facts::Rest do - it "should be a sublcass of Puppet::Indirector::REST" do - Puppet::Node::Facts::Rest.superclass.should equal(Puppet::Indirector::REST) - end -end diff --git a/spec/unit/indirector/facts/yaml_spec.rb b/spec/unit/indirector/facts/yaml_spec.rb new file mode 100755 index 000000000..8b49fa3b5 --- /dev/null +++ b/spec/unit/indirector/facts/yaml_spec.rb @@ -0,0 +1,26 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/node/facts' +require 'puppet/indirector/facts/yaml' + +describe Puppet::Node::Facts::Yaml do + it "should be a subclass of the Yaml terminus" do + Puppet::Node::Facts::Yaml.superclass.should equal(Puppet::Indirector::Yaml) + end + + + it "should have documentation" do + Puppet::Node::Facts::Yaml.doc.should_not be_nil + end + + it "should be registered with the facts indirection" do + indirection = Puppet::Indirector::Indirection.instance(:facts) + Puppet::Node::Facts::Yaml.indirection.should equal(indirection) + end + + it "should have its name set to :facts" do + Puppet::Node::Facts::Yaml.name.should == :yaml + end +end diff --git a/spec/unit/indirector/facts/yaml_spec_spec.rb b/spec/unit/indirector/facts/yaml_spec_spec.rb deleted file mode 100755 index 8b49fa3b5..000000000 --- a/spec/unit/indirector/facts/yaml_spec_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/node/facts' -require 'puppet/indirector/facts/yaml' - -describe Puppet::Node::Facts::Yaml do - it "should be a subclass of the Yaml terminus" do - Puppet::Node::Facts::Yaml.superclass.should equal(Puppet::Indirector::Yaml) - end - - - it "should have documentation" do - Puppet::Node::Facts::Yaml.doc.should_not be_nil - end - - it "should be registered with the facts indirection" do - indirection = Puppet::Indirector::Indirection.instance(:facts) - Puppet::Node::Facts::Yaml.indirection.should equal(indirection) - end - - it "should have its name set to :facts" do - Puppet::Node::Facts::Yaml.name.should == :yaml - end -end diff --git a/spec/unit/indirector/file_bucket_file/file_spec.rb b/spec/unit/indirector/file_bucket_file/file_spec.rb new file mode 100755 index 000000000..f821ca45d --- /dev/null +++ b/spec/unit/indirector/file_bucket_file/file_spec.rb @@ -0,0 +1,290 @@ +#!/usr/bin/env ruby + +require ::File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/file_bucket_file/file' + +describe Puppet::FileBucketFile::File do + it "should be a subclass of the Code terminus class" do + Puppet::FileBucketFile::File.superclass.should equal(Puppet::Indirector::Code) + end + + it "should have documentation" do + Puppet::FileBucketFile::File.doc.should be_instance_of(String) + end + + describe "when initializing" do + it "should use the filebucket settings section" do + Puppet.settings.expects(:use).with(:filebucket) + Puppet::FileBucketFile::File.new + end + end + + + describe "the find_by_checksum method" do + before do + # this is the default from spec_helper, but it keeps getting reset at odd times + Puppet[:bucketdir] = "/dev/null/bucket" + + @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' + + @contents = "file contents" + end + + it "should return nil if a file doesn't exist" do + ::File.expects(:exist?).with("#{@dir}/contents").returns false + + bucketfile = Puppet::FileBucketFile::File.new.send(:find_by_checksum, "{md5}#{@digest}", {}) + bucketfile.should == nil + end + + it "should find a filebucket if the file exists" do + ::File.expects(:exist?).with("#{@dir}/contents").returns true + ::File.expects(:exist?).with("#{@dir}/paths").returns false + ::File.expects(:read).with("#{@dir}/contents").returns @contents + + bucketfile = Puppet::FileBucketFile::File.new.send(:find_by_checksum, "{md5}#{@digest}", {}) + bucketfile.should_not == nil + end + + it "should load the paths" do + paths = ["path1", "path2"] + ::File.expects(:exist?).with("#{@dir}/contents").returns true + ::File.expects(:exist?).with("#{@dir}/paths").returns true + ::File.expects(:read).with("#{@dir}/contents").returns @contents + + mockfile = mock "file" + mockfile.expects(:readlines).returns( paths ) + ::File.expects(:open).with("#{@dir}/paths").yields mockfile + + Puppet::FileBucketFile::File.new.send(:find_by_checksum, "{md5}#{@digest}", {}).paths.should == paths + end + + end + + describe "when retrieving files" do + before :each do + Puppet.settings.stubs(:use) + @store = Puppet::FileBucketFile::File.new + + @digest = "70924d6fa4b2d745185fa4660703a5c0" + @sum = stub 'sum', :name => @digest + + @dir = "/what/ever" + + Puppet.stubs(:[]).with(:bucketdir).returns(@dir) + + @contents_path = '/what/ever/7/0/9/2/4/d/6/f/70924d6fa4b2d745185fa4660703a5c0/contents' + @paths_path = '/what/ever/7/0/9/2/4/d/6/f/70924d6fa4b2d745185fa4660703a5c0/paths' + + @request = stub 'request', :key => "md5/#{@digest}/remote/path", :options => {} + end + + it "should call find_by_checksum" do + @store.expects(:find_by_checksum).with{|x,opts| x == "{md5}#{@digest}"}.returns(false) + @store.find(@request) + end + + it "should look for the calculated path" do + ::File.expects(:exist?).with(@contents_path).returns(false) + @store.find(@request) + end + + it "should return an instance of Puppet::FileBucket::File created with the content if the file exists" do + content = "my content" + bucketfile = stub 'bucketfile' + bucketfile.stubs(:bucket_path) + bucketfile.stubs(:bucket_path=) + bucketfile.stubs(:checksum_data).returns(@digest) + bucketfile.stubs(:checksum).returns(@checksum) + + bucketfile.expects(:contents=).with(content) + Puppet::FileBucket::File.expects(:new).with(nil, {:checksum => "{md5}#{@digest}"}).yields(bucketfile).returns(bucketfile) + + ::File.expects(:exist?).with(@contents_path).returns(true) + ::File.expects(:exist?).with(@paths_path).returns(false) + ::File.expects(:read).with(@contents_path).returns(content) + + @store.find(@request).should equal(bucketfile) + end + + it "should return nil if no file is found" do + ::File.expects(:exist?).with(@contents_path).returns(false) + @store.find(@request).should be_nil + end + + it "should fail intelligently if a found file cannot be read" do + ::File.expects(:exist?).with(@contents_path).returns(true) + ::File.expects(:read).with(@contents_path).raises(RuntimeError) + proc { @store.find(@request) }.should raise_error(Puppet::Error) + end + + end + + describe "when determining file paths" do + before do + Puppet[:bucketdir] = '/dev/null/bucketdir' + @digest = 'DEADBEEFC0FFEE' + @bucket = stub_everything "bucket" + @bucket.expects(:checksum_data).returns(@digest) + end + + it "should use the value of the :bucketdir setting as the root directory" do + path = Puppet::FileBucketFile::File.new.send(:contents_path_for, @bucket) + path.should =~ %r{^/dev/null/bucketdir} + end + + it "should choose a path 8 directories deep with each directory name being the respective character in the filebucket" do + path = Puppet::FileBucketFile::File.new.send(:contents_path_for, @bucket) + dirs = @digest[0..7].split("").join(File::SEPARATOR) + path.should be_include(dirs) + end + + it "should use the full filebucket as the final directory name" do + path = Puppet::FileBucketFile::File.new.send(:contents_path_for, @bucket) + ::File.basename(::File.dirname(path)).should == @digest + end + + it "should use 'contents' as the actual file name" do + path = Puppet::FileBucketFile::File.new.send(:contents_path_for, @bucket) + ::File.basename(path).should == "contents" + end + + it "should use the bucketdir, the 8 sum character directories, the full filebucket, and 'contents' as the full file name" do + path = Puppet::FileBucketFile::File.new.send(:contents_path_for, @bucket) + path.should == ['/dev/null/bucketdir', @digest[0..7].split(""), @digest, "contents"].flatten.join(::File::SEPARATOR) + end + end + + describe "when saving files" do + before do + # this is the default from spec_helper, but it keeps getting reset at odd times + Puppet[:bucketdir] = "/dev/null/bucket" + + @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' + + @contents = "file contents" + + @bucket = stub "bucket file" + @bucket.stubs(:bucket_path) + @bucket.stubs(:checksum_data).returns(@digest) + @bucket.stubs(:path).returns(nil) + @bucket.stubs(:checksum).returns(nil) + @bucket.stubs(:contents).returns("file contents") + end + + it "should save the contents to the calculated path" do + ::File.stubs(:directory?).with(@dir).returns(true) + ::File.expects(:exist?).with("#{@dir}/contents").returns false + + mockfile = mock "file" + mockfile.expects(:print).with(@contents) + ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440).yields(mockfile) + + Puppet::FileBucketFile::File.new.send(:save_to_disk, @bucket) + end + + it "should make any directories necessary for storage" do + FileUtils.expects(:mkdir_p).with do |arg| + ::File.umask == 0007 and arg == @dir + end + ::File.expects(:directory?).with(@dir).returns(false) + ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) + ::File.expects(:exist?).with("#{@dir}/contents").returns false + + Puppet::FileBucketFile::File.new.send(:save_to_disk, @bucket) + end + end + + + describe "when verifying identical files" do + before do + # this is the default from spec_helper, but it keeps getting reset at odd times + Puppet[:bucketdir] = "/dev/null/bucket" + + @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" + @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' + + @contents = "file contents" + + @bucket = stub "bucket file" + @bucket.stubs(:bucket_path) + @bucket.stubs(:checksum).returns(@checksum) + @bucket.stubs(:checksum_data).returns(@digest) + @bucket.stubs(:path).returns(nil) + @bucket.stubs(:contents).returns("file contents") + end + + it "should raise an error if the files don't match" do + File.expects(:read).with("#{@dir}/contents").returns("corrupt contents") + lambda{ Puppet::FileBucketFile::File.new.send(:verify_identical_file!, @bucket) }.should raise_error(Puppet::FileBucket::BucketError) + end + + it "should do nothing if the files match" do + File.expects(:read).with("#{@dir}/contents").returns("file contents") + Puppet::FileBucketFile::File.new.send(:verify_identical_file!, @bucket) + end + + end + + + describe "when writing to the paths file" do + before do + Puppet[:bucketdir] = '/dev/null/bucketdir' + @digest = '70924d6fa4b2d745185fa4660703a5c0' + @bucket = stub_everything "bucket" + + @paths_path = '/dev/null/bucketdir/7/0/9/2/4/d/6/f/70924d6fa4b2d745185fa4660703a5c0/paths' + + @paths = [] + @bucket.stubs(:paths).returns(@paths) + @bucket.stubs(:checksum_data).returns(@digest) + end + + it "should create a file if it doesn't exist" do + @bucket.expects(:path).returns('path/to/save').at_least_once + File.expects(:exist?).with(@paths_path).returns(false) + file = stub "file" + file.expects(:puts).with('path/to/save') + File.expects(:open).with(@paths_path, ::File::WRONLY|::File::CREAT|::File::APPEND).yields(file) + + Puppet::FileBucketFile::File.new.send(:save_path_to_paths_file, @bucket) + end + + it "should append to a file if it exists" do + @bucket.expects(:path).returns('path/to/save').at_least_once + File.expects(:exist?).with(@paths_path).returns(true) + old_file = stub "file" + old_file.stubs(:readlines).returns [] + File.expects(:open).with(@paths_path).yields(old_file) + + file = stub "file" + file.expects(:puts).with('path/to/save') + File.expects(:open).with(@paths_path, ::File::WRONLY|::File::CREAT|::File::APPEND).yields(file) + + Puppet::FileBucketFile::File.new.send(:save_path_to_paths_file, @bucket) + end + + it "should not alter a file if it already contains the path" do + @bucket.expects(:path).returns('path/to/save').at_least_once + File.expects(:exist?).with(@paths_path).returns(true) + old_file = stub "file" + old_file.stubs(:readlines).returns ["path/to/save\n"] + File.expects(:open).with(@paths_path).yields(old_file) + + Puppet::FileBucketFile::File.new.send(:save_path_to_paths_file, @bucket) + end + + it "should do nothing if there is no path" do + @bucket.expects(:path).returns(nil).at_least_once + + Puppet::FileBucketFile::File.new.send(:save_path_to_paths_file, @bucket) + end + end + +end diff --git a/spec/unit/indirector/file_bucket_file/file_spec_spec.rb b/spec/unit/indirector/file_bucket_file/file_spec_spec.rb deleted file mode 100755 index f821ca45d..000000000 --- a/spec/unit/indirector/file_bucket_file/file_spec_spec.rb +++ /dev/null @@ -1,290 +0,0 @@ -#!/usr/bin/env ruby - -require ::File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/file_bucket_file/file' - -describe Puppet::FileBucketFile::File do - it "should be a subclass of the Code terminus class" do - Puppet::FileBucketFile::File.superclass.should equal(Puppet::Indirector::Code) - end - - it "should have documentation" do - Puppet::FileBucketFile::File.doc.should be_instance_of(String) - end - - describe "when initializing" do - it "should use the filebucket settings section" do - Puppet.settings.expects(:use).with(:filebucket) - Puppet::FileBucketFile::File.new - end - end - - - describe "the find_by_checksum method" do - before do - # this is the default from spec_helper, but it keeps getting reset at odd times - Puppet[:bucketdir] = "/dev/null/bucket" - - @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' - - @contents = "file contents" - end - - it "should return nil if a file doesn't exist" do - ::File.expects(:exist?).with("#{@dir}/contents").returns false - - bucketfile = Puppet::FileBucketFile::File.new.send(:find_by_checksum, "{md5}#{@digest}", {}) - bucketfile.should == nil - end - - it "should find a filebucket if the file exists" do - ::File.expects(:exist?).with("#{@dir}/contents").returns true - ::File.expects(:exist?).with("#{@dir}/paths").returns false - ::File.expects(:read).with("#{@dir}/contents").returns @contents - - bucketfile = Puppet::FileBucketFile::File.new.send(:find_by_checksum, "{md5}#{@digest}", {}) - bucketfile.should_not == nil - end - - it "should load the paths" do - paths = ["path1", "path2"] - ::File.expects(:exist?).with("#{@dir}/contents").returns true - ::File.expects(:exist?).with("#{@dir}/paths").returns true - ::File.expects(:read).with("#{@dir}/contents").returns @contents - - mockfile = mock "file" - mockfile.expects(:readlines).returns( paths ) - ::File.expects(:open).with("#{@dir}/paths").yields mockfile - - Puppet::FileBucketFile::File.new.send(:find_by_checksum, "{md5}#{@digest}", {}).paths.should == paths - end - - end - - describe "when retrieving files" do - before :each do - Puppet.settings.stubs(:use) - @store = Puppet::FileBucketFile::File.new - - @digest = "70924d6fa4b2d745185fa4660703a5c0" - @sum = stub 'sum', :name => @digest - - @dir = "/what/ever" - - Puppet.stubs(:[]).with(:bucketdir).returns(@dir) - - @contents_path = '/what/ever/7/0/9/2/4/d/6/f/70924d6fa4b2d745185fa4660703a5c0/contents' - @paths_path = '/what/ever/7/0/9/2/4/d/6/f/70924d6fa4b2d745185fa4660703a5c0/paths' - - @request = stub 'request', :key => "md5/#{@digest}/remote/path", :options => {} - end - - it "should call find_by_checksum" do - @store.expects(:find_by_checksum).with{|x,opts| x == "{md5}#{@digest}"}.returns(false) - @store.find(@request) - end - - it "should look for the calculated path" do - ::File.expects(:exist?).with(@contents_path).returns(false) - @store.find(@request) - end - - it "should return an instance of Puppet::FileBucket::File created with the content if the file exists" do - content = "my content" - bucketfile = stub 'bucketfile' - bucketfile.stubs(:bucket_path) - bucketfile.stubs(:bucket_path=) - bucketfile.stubs(:checksum_data).returns(@digest) - bucketfile.stubs(:checksum).returns(@checksum) - - bucketfile.expects(:contents=).with(content) - Puppet::FileBucket::File.expects(:new).with(nil, {:checksum => "{md5}#{@digest}"}).yields(bucketfile).returns(bucketfile) - - ::File.expects(:exist?).with(@contents_path).returns(true) - ::File.expects(:exist?).with(@paths_path).returns(false) - ::File.expects(:read).with(@contents_path).returns(content) - - @store.find(@request).should equal(bucketfile) - end - - it "should return nil if no file is found" do - ::File.expects(:exist?).with(@contents_path).returns(false) - @store.find(@request).should be_nil - end - - it "should fail intelligently if a found file cannot be read" do - ::File.expects(:exist?).with(@contents_path).returns(true) - ::File.expects(:read).with(@contents_path).raises(RuntimeError) - proc { @store.find(@request) }.should raise_error(Puppet::Error) - end - - end - - describe "when determining file paths" do - before do - Puppet[:bucketdir] = '/dev/null/bucketdir' - @digest = 'DEADBEEFC0FFEE' - @bucket = stub_everything "bucket" - @bucket.expects(:checksum_data).returns(@digest) - end - - it "should use the value of the :bucketdir setting as the root directory" do - path = Puppet::FileBucketFile::File.new.send(:contents_path_for, @bucket) - path.should =~ %r{^/dev/null/bucketdir} - end - - it "should choose a path 8 directories deep with each directory name being the respective character in the filebucket" do - path = Puppet::FileBucketFile::File.new.send(:contents_path_for, @bucket) - dirs = @digest[0..7].split("").join(File::SEPARATOR) - path.should be_include(dirs) - end - - it "should use the full filebucket as the final directory name" do - path = Puppet::FileBucketFile::File.new.send(:contents_path_for, @bucket) - ::File.basename(::File.dirname(path)).should == @digest - end - - it "should use 'contents' as the actual file name" do - path = Puppet::FileBucketFile::File.new.send(:contents_path_for, @bucket) - ::File.basename(path).should == "contents" - end - - it "should use the bucketdir, the 8 sum character directories, the full filebucket, and 'contents' as the full file name" do - path = Puppet::FileBucketFile::File.new.send(:contents_path_for, @bucket) - path.should == ['/dev/null/bucketdir', @digest[0..7].split(""), @digest, "contents"].flatten.join(::File::SEPARATOR) - end - end - - describe "when saving files" do - before do - # this is the default from spec_helper, but it keeps getting reset at odd times - Puppet[:bucketdir] = "/dev/null/bucket" - - @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' - - @contents = "file contents" - - @bucket = stub "bucket file" - @bucket.stubs(:bucket_path) - @bucket.stubs(:checksum_data).returns(@digest) - @bucket.stubs(:path).returns(nil) - @bucket.stubs(:checksum).returns(nil) - @bucket.stubs(:contents).returns("file contents") - end - - it "should save the contents to the calculated path" do - ::File.stubs(:directory?).with(@dir).returns(true) - ::File.expects(:exist?).with("#{@dir}/contents").returns false - - mockfile = mock "file" - mockfile.expects(:print).with(@contents) - ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440).yields(mockfile) - - Puppet::FileBucketFile::File.new.send(:save_to_disk, @bucket) - end - - it "should make any directories necessary for storage" do - FileUtils.expects(:mkdir_p).with do |arg| - ::File.umask == 0007 and arg == @dir - end - ::File.expects(:directory?).with(@dir).returns(false) - ::File.expects(:open).with("#{@dir}/contents", ::File::WRONLY|::File::CREAT, 0440) - ::File.expects(:exist?).with("#{@dir}/contents").returns false - - Puppet::FileBucketFile::File.new.send(:save_to_disk, @bucket) - end - end - - - describe "when verifying identical files" do - before do - # this is the default from spec_helper, but it keeps getting reset at odd times - Puppet[:bucketdir] = "/dev/null/bucket" - - @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' - - @contents = "file contents" - - @bucket = stub "bucket file" - @bucket.stubs(:bucket_path) - @bucket.stubs(:checksum).returns(@checksum) - @bucket.stubs(:checksum_data).returns(@digest) - @bucket.stubs(:path).returns(nil) - @bucket.stubs(:contents).returns("file contents") - end - - it "should raise an error if the files don't match" do - File.expects(:read).with("#{@dir}/contents").returns("corrupt contents") - lambda{ Puppet::FileBucketFile::File.new.send(:verify_identical_file!, @bucket) }.should raise_error(Puppet::FileBucket::BucketError) - end - - it "should do nothing if the files match" do - File.expects(:read).with("#{@dir}/contents").returns("file contents") - Puppet::FileBucketFile::File.new.send(:verify_identical_file!, @bucket) - end - - end - - - describe "when writing to the paths file" do - before do - Puppet[:bucketdir] = '/dev/null/bucketdir' - @digest = '70924d6fa4b2d745185fa4660703a5c0' - @bucket = stub_everything "bucket" - - @paths_path = '/dev/null/bucketdir/7/0/9/2/4/d/6/f/70924d6fa4b2d745185fa4660703a5c0/paths' - - @paths = [] - @bucket.stubs(:paths).returns(@paths) - @bucket.stubs(:checksum_data).returns(@digest) - end - - it "should create a file if it doesn't exist" do - @bucket.expects(:path).returns('path/to/save').at_least_once - File.expects(:exist?).with(@paths_path).returns(false) - file = stub "file" - file.expects(:puts).with('path/to/save') - File.expects(:open).with(@paths_path, ::File::WRONLY|::File::CREAT|::File::APPEND).yields(file) - - Puppet::FileBucketFile::File.new.send(:save_path_to_paths_file, @bucket) - end - - it "should append to a file if it exists" do - @bucket.expects(:path).returns('path/to/save').at_least_once - File.expects(:exist?).with(@paths_path).returns(true) - old_file = stub "file" - old_file.stubs(:readlines).returns [] - File.expects(:open).with(@paths_path).yields(old_file) - - file = stub "file" - file.expects(:puts).with('path/to/save') - File.expects(:open).with(@paths_path, ::File::WRONLY|::File::CREAT|::File::APPEND).yields(file) - - Puppet::FileBucketFile::File.new.send(:save_path_to_paths_file, @bucket) - end - - it "should not alter a file if it already contains the path" do - @bucket.expects(:path).returns('path/to/save').at_least_once - File.expects(:exist?).with(@paths_path).returns(true) - old_file = stub "file" - old_file.stubs(:readlines).returns ["path/to/save\n"] - File.expects(:open).with(@paths_path).yields(old_file) - - Puppet::FileBucketFile::File.new.send(:save_path_to_paths_file, @bucket) - end - - it "should do nothing if there is no path" do - @bucket.expects(:path).returns(nil).at_least_once - - Puppet::FileBucketFile::File.new.send(:save_path_to_paths_file, @bucket) - end - end - -end diff --git a/spec/unit/indirector/file_bucket_file/rest_spec.rb b/spec/unit/indirector/file_bucket_file/rest_spec.rb new file mode 100755 index 000000000..3aacd3ca4 --- /dev/null +++ b/spec/unit/indirector/file_bucket_file/rest_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/indirector/file_bucket_file/rest' + +describe Puppet::FileBucketFile::Rest do + it "should be a sublcass of Puppet::Indirector::REST" do + Puppet::FileBucketFile::Rest.superclass.should equal(Puppet::Indirector::REST) + end +end diff --git a/spec/unit/indirector/file_bucket_file/rest_spec_spec.rb b/spec/unit/indirector/file_bucket_file/rest_spec_spec.rb deleted file mode 100755 index 3aacd3ca4..000000000 --- a/spec/unit/indirector/file_bucket_file/rest_spec_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/indirector/file_bucket_file/rest' - -describe Puppet::FileBucketFile::Rest do - it "should be a sublcass of Puppet::Indirector::REST" do - Puppet::FileBucketFile::Rest.superclass.should equal(Puppet::Indirector::REST) - end -end diff --git a/spec/unit/indirector/file_content/file_server_spec.rb b/spec/unit/indirector/file_content/file_server_spec.rb new file mode 100755 index 000000000..a09c7e2f7 --- /dev/null +++ b/spec/unit/indirector/file_content/file_server_spec.rb @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/file_content/file_server' + +describe Puppet::Indirector::FileContent::FileServer do + it "should be registered with the file_content indirection" do + Puppet::Indirector::Terminus.terminus_class(:file_content, :file_server).should equal(Puppet::Indirector::FileContent::FileServer) + end + + it "should be a subclass of the FileServer terminus" do + Puppet::Indirector::FileContent::FileServer.superclass.should equal(Puppet::Indirector::FileServer) + end +end diff --git a/spec/unit/indirector/file_content/file_server_spec_spec.rb b/spec/unit/indirector/file_content/file_server_spec_spec.rb deleted file mode 100755 index a09c7e2f7..000000000 --- a/spec/unit/indirector/file_content/file_server_spec_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-10-18. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/file_content/file_server' - -describe Puppet::Indirector::FileContent::FileServer do - it "should be registered with the file_content indirection" do - Puppet::Indirector::Terminus.terminus_class(:file_content, :file_server).should equal(Puppet::Indirector::FileContent::FileServer) - end - - it "should be a subclass of the FileServer terminus" do - Puppet::Indirector::FileContent::FileServer.superclass.should equal(Puppet::Indirector::FileServer) - end -end diff --git a/spec/unit/indirector/file_content/file_spec.rb b/spec/unit/indirector/file_content/file_spec.rb new file mode 100755 index 000000000..04656e0ed --- /dev/null +++ b/spec/unit/indirector/file_content/file_spec.rb @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/file_content/file' + +describe Puppet::Indirector::FileContent::File do + it "should be registered with the file_content indirection" do + Puppet::Indirector::Terminus.terminus_class(:file_content, :file).should equal(Puppet::Indirector::FileContent::File) + end + + it "should be a subclass of the DirectFileServer terminus" do + Puppet::Indirector::FileContent::File.superclass.should equal(Puppet::Indirector::DirectFileServer) + end +end diff --git a/spec/unit/indirector/file_content/file_spec_spec.rb b/spec/unit/indirector/file_content/file_spec_spec.rb deleted file mode 100755 index 04656e0ed..000000000 --- a/spec/unit/indirector/file_content/file_spec_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-10-18. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/file_content/file' - -describe Puppet::Indirector::FileContent::File do - it "should be registered with the file_content indirection" do - Puppet::Indirector::Terminus.terminus_class(:file_content, :file).should equal(Puppet::Indirector::FileContent::File) - end - - it "should be a subclass of the DirectFileServer terminus" do - Puppet::Indirector::FileContent::File.superclass.should equal(Puppet::Indirector::DirectFileServer) - end -end diff --git a/spec/unit/indirector/file_content/rest_spec.rb b/spec/unit/indirector/file_content/rest_spec.rb new file mode 100755 index 000000000..afb674e0a --- /dev/null +++ b/spec/unit/indirector/file_content/rest_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/file_content' + +describe "Puppet::Indirector::Content::Rest" do + it "should add the node's cert name to the arguments" + + it "should set the content type to text/plain" +end diff --git a/spec/unit/indirector/file_content/rest_spec_spec.rb b/spec/unit/indirector/file_content/rest_spec_spec.rb deleted file mode 100755 index afb674e0a..000000000 --- a/spec/unit/indirector/file_content/rest_spec_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/file_content' - -describe "Puppet::Indirector::Content::Rest" do - it "should add the node's cert name to the arguments" - - it "should set the content type to text/plain" -end diff --git a/spec/unit/indirector/file_metadata/file_server_spec.rb b/spec/unit/indirector/file_metadata/file_server_spec.rb new file mode 100755 index 000000000..40232a108 --- /dev/null +++ b/spec/unit/indirector/file_metadata/file_server_spec.rb @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/file_metadata/file_server' + +describe Puppet::Indirector::FileMetadata::FileServer do + it "should be registered with the file_metadata indirection" do + Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file_server).should equal(Puppet::Indirector::FileMetadata::FileServer) + end + + it "should be a subclass of the FileServer terminus" do + Puppet::Indirector::FileMetadata::FileServer.superclass.should equal(Puppet::Indirector::FileServer) + end +end diff --git a/spec/unit/indirector/file_metadata/file_server_spec_spec.rb b/spec/unit/indirector/file_metadata/file_server_spec_spec.rb deleted file mode 100755 index 40232a108..000000000 --- a/spec/unit/indirector/file_metadata/file_server_spec_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-10-18. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/file_metadata/file_server' - -describe Puppet::Indirector::FileMetadata::FileServer do - it "should be registered with the file_metadata indirection" do - Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file_server).should equal(Puppet::Indirector::FileMetadata::FileServer) - end - - it "should be a subclass of the FileServer terminus" do - Puppet::Indirector::FileMetadata::FileServer.superclass.should equal(Puppet::Indirector::FileServer) - end -end diff --git a/spec/unit/indirector/file_metadata/file_spec.rb b/spec/unit/indirector/file_metadata/file_spec.rb new file mode 100755 index 000000000..a096d469d --- /dev/null +++ b/spec/unit/indirector/file_metadata/file_spec.rb @@ -0,0 +1,52 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/file_metadata/file' + +describe Puppet::Indirector::FileMetadata::File do + it "should be registered with the file_metadata indirection" do + Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file).should equal(Puppet::Indirector::FileMetadata::File) + end + + it "should be a subclass of the DirectFileServer terminus" do + Puppet::Indirector::FileMetadata::File.superclass.should equal(Puppet::Indirector::DirectFileServer) + end + + describe "when creating the instance for a single found file" do + before do + @metadata = Puppet::Indirector::FileMetadata::File.new + @uri = "file:///my/local" + @data = mock 'metadata' + @data.stubs(:collect) + FileTest.expects(:exists?).with("/my/local").returns true + + @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri) + end + + it "should collect its attributes when a file is found" do + @data.expects(:collect) + + Puppet::FileServing::Metadata.expects(:new).returns(@data) + @metadata.find(@request).should == @data + end + end + + describe "when searching for multiple files" do + before do + @metadata = Puppet::Indirector::FileMetadata::File.new + @uri = "file:///my/local" + + @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri) + end + + it "should collect the attributes of the instances returned" do + FileTest.expects(:exists?).with("/my/local").returns true + @metadata.expects(:path2instances).returns( [mock("one", :collect => nil), mock("two", :collect => nil)] ) + @metadata.search(@request) + end + end +end diff --git a/spec/unit/indirector/file_metadata/file_spec_spec.rb b/spec/unit/indirector/file_metadata/file_spec_spec.rb deleted file mode 100755 index a096d469d..000000000 --- a/spec/unit/indirector/file_metadata/file_spec_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-10-18. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/file_metadata/file' - -describe Puppet::Indirector::FileMetadata::File do - it "should be registered with the file_metadata indirection" do - Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file).should equal(Puppet::Indirector::FileMetadata::File) - end - - it "should be a subclass of the DirectFileServer terminus" do - Puppet::Indirector::FileMetadata::File.superclass.should equal(Puppet::Indirector::DirectFileServer) - end - - describe "when creating the instance for a single found file" do - before do - @metadata = Puppet::Indirector::FileMetadata::File.new - @uri = "file:///my/local" - @data = mock 'metadata' - @data.stubs(:collect) - FileTest.expects(:exists?).with("/my/local").returns true - - @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri) - end - - it "should collect its attributes when a file is found" do - @data.expects(:collect) - - Puppet::FileServing::Metadata.expects(:new).returns(@data) - @metadata.find(@request).should == @data - end - end - - describe "when searching for multiple files" do - before do - @metadata = Puppet::Indirector::FileMetadata::File.new - @uri = "file:///my/local" - - @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri) - end - - it "should collect the attributes of the instances returned" do - FileTest.expects(:exists?).with("/my/local").returns true - @metadata.expects(:path2instances).returns( [mock("one", :collect => nil), mock("two", :collect => nil)] ) - @metadata.search(@request) - end - end -end diff --git a/spec/unit/indirector/file_metadata/rest_spec.rb b/spec/unit/indirector/file_metadata/rest_spec.rb new file mode 100755 index 000000000..e93406f88 --- /dev/null +++ b/spec/unit/indirector/file_metadata/rest_spec.rb @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/file_metadata' + +describe "Puppet::Indirector::Metadata::Rest" do + it "should add the node's cert name to the arguments" +end diff --git a/spec/unit/indirector/file_metadata/rest_spec_spec.rb b/spec/unit/indirector/file_metadata/rest_spec_spec.rb deleted file mode 100755 index e93406f88..000000000 --- a/spec/unit/indirector/file_metadata/rest_spec_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/file_metadata' - -describe "Puppet::Indirector::Metadata::Rest" do - it "should add the node's cert name to the arguments" -end diff --git a/spec/unit/indirector/file_server_spec.rb b/spec/unit/indirector/file_server_spec.rb new file mode 100755 index 000000000..912695e27 --- /dev/null +++ b/spec/unit/indirector/file_server_spec.rb @@ -0,0 +1,267 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-10-19. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/indirector/file_server' +require 'puppet/file_serving/configuration' + +describe Puppet::Indirector::FileServer do + + before :each do + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @model = mock 'model' + @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model + Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) + + @file_server_class = Class.new(Puppet::Indirector::FileServer) do + def self.to_s + "Testing::Mytype" + end + end + + @file_server = @file_server_class.new + + @uri = "puppet://host/my/local/file" + @configuration = mock 'configuration' + Puppet::FileServing::Configuration.stubs(:create).returns(@configuration) + + @request = Puppet::Indirector::Request.new(:myind, :mymethod, @uri, :environment => "myenv") + end + + describe "when finding files" do + before do + @mount = stub 'mount', :find => nil + @instance = stub('instance', :links= => nil, :collect => nil) + end + + it "should use the configuration to find the mount and relative path" do + @configuration.expects(:split_path).with(@request) + + @file_server.find(@request) + end + + it "should return nil if it cannot find the mount" do + @configuration.expects(:split_path).with(@request).returns(nil, nil) + + @file_server.find(@request).should be_nil + end + + it "should use the mount to find the full path" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:find).with { |key, request| key == "rel/path" } + + @file_server.find(@request) + end + + it "should pass the request when finding a file" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:find).with { |key, request| request == @request } + + @file_server.find(@request) + end + + it "should return nil if it cannot find a full path" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:find).with { |key, request| key == "rel/path" }.returns nil + + @file_server.find(@request).should be_nil + end + + it "should create an instance with the found path" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" + + @model.expects(:new).with("/my/file").returns @instance + + @file_server.find(@request).should equal(@instance) + end + + it "should set 'links' on the instance if it is set in the request options" do + @request.options[:links] = true + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" + + @model.expects(:new).with("/my/file").returns @instance + + @instance.expects(:links=).with(true) + + @file_server.find(@request).should equal(@instance) + end + + it "should collect the instance" do + @request.options[:links] = true + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" + + @model.expects(:new).with("/my/file").returns @instance + + @instance.expects(:collect) + + @file_server.find(@request).should equal(@instance) + end + end + + describe "when searching for instances" do + before do + @mount = stub 'mount', :search => nil + @instance = stub('instance', :links= => nil, :collect => nil) + end + + it "should use the configuration to search the mount and relative path" do + @configuration.expects(:split_path).with(@request) + + @file_server.search(@request) + end + + it "should return nil if it cannot search the mount" do + @configuration.expects(:split_path).with(@request).returns(nil, nil) + + @file_server.search(@request).should be_nil + end + + it "should use the mount to search for the full paths" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:search).with { |key, request| key == "rel/path" } + + @file_server.search(@request) + end + + it "should pass the request" do + @configuration.stubs(:split_path).returns([@mount, "rel/path"]) + + @mount.expects(:search).with { |key, request| request == @request } + + @file_server.search(@request) + end + + it "should return nil if searching does not find any full paths" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:search).with { |key, request| key == "rel/path" }.returns nil + + @file_server.search(@request).should be_nil + end + + it "should create a fileset with each returned path and merge them" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:search).with { |key, request| key == "rel/path" }.returns %w{/one /two} + + FileTest.stubs(:exist?).returns true + + one = mock 'fileset_one' + Puppet::FileServing::Fileset.expects(:new).with("/one", @request).returns(one) + two = mock 'fileset_two' + Puppet::FileServing::Fileset.expects(:new).with("/two", @request).returns(two) + + Puppet::FileServing::Fileset.expects(:merge).with(one, two).returns [] + + @file_server.search(@request) + end + + it "should create an instance with each path resulting from the merger of the filesets" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] + + FileTest.stubs(:exist?).returns true + + Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one", "two" => "/two") + + one = stub 'one', :collect => nil + @model.expects(:new).with("/one", :relative_path => "one").returns one + + two = stub 'two', :collect => nil + @model.expects(:new).with("/two", :relative_path => "two").returns two + + # order can't be guaranteed + result = @file_server.search(@request) + result.should be_include(one) + result.should be_include(two) + result.length.should == 2 + end + + it "should set 'links' on the instances if it is set in the request options" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] + + FileTest.stubs(:exist?).returns true + + Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") + + one = stub 'one', :collect => nil + @model.expects(:new).with("/one", :relative_path => "one").returns one + one.expects(:links=).with true + + @request.options[:links] = true + + @file_server.search(@request) + end + + it "should collect the instances" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:search).with { |key, options| key == "rel/path" }.returns [] + + FileTest.stubs(:exist?).returns true + + Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") + + one = mock 'one' + @model.expects(:new).with("/one", :relative_path => "one").returns one + one.expects(:collect) + + @file_server.search(@request) + end + end + + describe "when checking authorization" do + before do + @request.method = :find + end + + it "should return false when destroying" do + @request.method = :destroy + @file_server.should_not be_authorized(@request) + end + + it "should return false when saving" do + @request.method = :save + @file_server.should_not be_authorized(@request) + end + + it "should use the configuration to find the mount and relative path" do + @configuration.expects(:split_path).with(@request) + + @file_server.authorized?(@request) + end + + it "should return false if it cannot find the mount" do + @configuration.expects(:split_path).with(@request).returns(nil, nil) + + @file_server.should_not be_authorized(@request) + end + + it "should return the results of asking the mount whether the node and IP are authorized" do + @mount = stub 'mount' + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @request.stubs(:node).returns("mynode") + @request.stubs(:ip).returns("myip") + @mount.expects(:allowed?).with("mynode", "myip").returns "something" + + @file_server.authorized?(@request).should == "something" + end + end +end diff --git a/spec/unit/indirector/file_server_spec_spec.rb b/spec/unit/indirector/file_server_spec_spec.rb deleted file mode 100755 index 912695e27..000000000 --- a/spec/unit/indirector/file_server_spec_spec.rb +++ /dev/null @@ -1,267 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-10-19. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/indirector/file_server' -require 'puppet/file_serving/configuration' - -describe Puppet::Indirector::FileServer do - - before :each do - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @model = mock 'model' - @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model - Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - - @file_server_class = Class.new(Puppet::Indirector::FileServer) do - def self.to_s - "Testing::Mytype" - end - end - - @file_server = @file_server_class.new - - @uri = "puppet://host/my/local/file" - @configuration = mock 'configuration' - Puppet::FileServing::Configuration.stubs(:create).returns(@configuration) - - @request = Puppet::Indirector::Request.new(:myind, :mymethod, @uri, :environment => "myenv") - end - - describe "when finding files" do - before do - @mount = stub 'mount', :find => nil - @instance = stub('instance', :links= => nil, :collect => nil) - end - - it "should use the configuration to find the mount and relative path" do - @configuration.expects(:split_path).with(@request) - - @file_server.find(@request) - end - - it "should return nil if it cannot find the mount" do - @configuration.expects(:split_path).with(@request).returns(nil, nil) - - @file_server.find(@request).should be_nil - end - - it "should use the mount to find the full path" do - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:find).with { |key, request| key == "rel/path" } - - @file_server.find(@request) - end - - it "should pass the request when finding a file" do - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:find).with { |key, request| request == @request } - - @file_server.find(@request) - end - - it "should return nil if it cannot find a full path" do - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:find).with { |key, request| key == "rel/path" }.returns nil - - @file_server.find(@request).should be_nil - end - - it "should create an instance with the found path" do - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" - - @model.expects(:new).with("/my/file").returns @instance - - @file_server.find(@request).should equal(@instance) - end - - it "should set 'links' on the instance if it is set in the request options" do - @request.options[:links] = true - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" - - @model.expects(:new).with("/my/file").returns @instance - - @instance.expects(:links=).with(true) - - @file_server.find(@request).should equal(@instance) - end - - it "should collect the instance" do - @request.options[:links] = true - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" - - @model.expects(:new).with("/my/file").returns @instance - - @instance.expects(:collect) - - @file_server.find(@request).should equal(@instance) - end - end - - describe "when searching for instances" do - before do - @mount = stub 'mount', :search => nil - @instance = stub('instance', :links= => nil, :collect => nil) - end - - it "should use the configuration to search the mount and relative path" do - @configuration.expects(:split_path).with(@request) - - @file_server.search(@request) - end - - it "should return nil if it cannot search the mount" do - @configuration.expects(:split_path).with(@request).returns(nil, nil) - - @file_server.search(@request).should be_nil - end - - it "should use the mount to search for the full paths" do - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:search).with { |key, request| key == "rel/path" } - - @file_server.search(@request) - end - - it "should pass the request" do - @configuration.stubs(:split_path).returns([@mount, "rel/path"]) - - @mount.expects(:search).with { |key, request| request == @request } - - @file_server.search(@request) - end - - it "should return nil if searching does not find any full paths" do - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:search).with { |key, request| key == "rel/path" }.returns nil - - @file_server.search(@request).should be_nil - end - - it "should create a fileset with each returned path and merge them" do - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:search).with { |key, request| key == "rel/path" }.returns %w{/one /two} - - FileTest.stubs(:exist?).returns true - - one = mock 'fileset_one' - Puppet::FileServing::Fileset.expects(:new).with("/one", @request).returns(one) - two = mock 'fileset_two' - Puppet::FileServing::Fileset.expects(:new).with("/two", @request).returns(two) - - Puppet::FileServing::Fileset.expects(:merge).with(one, two).returns [] - - @file_server.search(@request) - end - - it "should create an instance with each path resulting from the merger of the filesets" do - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] - - FileTest.stubs(:exist?).returns true - - Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one", "two" => "/two") - - one = stub 'one', :collect => nil - @model.expects(:new).with("/one", :relative_path => "one").returns one - - two = stub 'two', :collect => nil - @model.expects(:new).with("/two", :relative_path => "two").returns two - - # order can't be guaranteed - result = @file_server.search(@request) - result.should be_include(one) - result.should be_include(two) - result.length.should == 2 - end - - it "should set 'links' on the instances if it is set in the request options" do - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] - - FileTest.stubs(:exist?).returns true - - Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") - - one = stub 'one', :collect => nil - @model.expects(:new).with("/one", :relative_path => "one").returns one - one.expects(:links=).with true - - @request.options[:links] = true - - @file_server.search(@request) - end - - it "should collect the instances" do - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @mount.expects(:search).with { |key, options| key == "rel/path" }.returns [] - - FileTest.stubs(:exist?).returns true - - Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") - - one = mock 'one' - @model.expects(:new).with("/one", :relative_path => "one").returns one - one.expects(:collect) - - @file_server.search(@request) - end - end - - describe "when checking authorization" do - before do - @request.method = :find - end - - it "should return false when destroying" do - @request.method = :destroy - @file_server.should_not be_authorized(@request) - end - - it "should return false when saving" do - @request.method = :save - @file_server.should_not be_authorized(@request) - end - - it "should use the configuration to find the mount and relative path" do - @configuration.expects(:split_path).with(@request) - - @file_server.authorized?(@request) - end - - it "should return false if it cannot find the mount" do - @configuration.expects(:split_path).with(@request).returns(nil, nil) - - @file_server.should_not be_authorized(@request) - end - - it "should return the results of asking the mount whether the node and IP are authorized" do - @mount = stub 'mount' - @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) - - @request.stubs(:node).returns("mynode") - @request.stubs(:ip).returns("myip") - @mount.expects(:allowed?).with("mynode", "myip").returns "something" - - @file_server.authorized?(@request).should == "something" - end - end -end diff --git a/spec/unit/indirector/file_spec.rb b/spec/unit/indirector/file_spec.rb new file mode 100755 index 000000000..207d897bf --- /dev/null +++ b/spec/unit/indirector/file_spec.rb @@ -0,0 +1,181 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/indirector/file' + + +describe Puppet::Indirector::File do + before :each do + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @model = mock 'model' + @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model + Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) + + @file_class = Class.new(Puppet::Indirector::File) do + def self.to_s + "Testing::Mytype" + end + end + + @searcher = @file_class.new + + @path = "/my/file" + @dir = "/my" + + @request = stub 'request', :key => @path + end + + describe "when finding files" do + it "should provide a method to return file contents at a specified path" do + @searcher.should respond_to(:find) + end + + it "should use the server data directory plus the indirection name if the process mode is master" do + Puppet.mode.expects(:master?).returns true + Puppet.settings.expects(:value).with(:server_datadir).returns "/my/dir" + + @searcher.data_directory.should == File.join("/my/dir", "mystuff") + end + + it "should use the client data directory plus the indirection name if the process mode is not master" do + Puppet.mode.expects(:master?).returns false + Puppet.settings.expects(:value).with(:client_datadir).returns "/my/dir" + + @searcher.data_directory.should == File.join("/my/dir", "mystuff") + end + + it "should use the newest file in the data directory matching the indirection key without extension" do + @searcher.expects(:data_directory).returns "/data/dir" + @request.stubs(:key).returns "foo" + Dir.expects(:glob).with("/data/dir/foo.*").returns %w{/data1.stuff /data2.stuff} + + stat1 = stub 'data1', :mtime => (Time.now - 5) + stat2 = stub 'data2', :mtime => Time.now + File.expects(:stat).with("/data1.stuff").returns stat1 + File.expects(:stat).with("/data2.stuff").returns stat2 + + @searcher.latest_path(@request).should == "/data2.stuff" + end + + it "should return nil when no files are found" do + @searcher.stubs(:latest_path).returns nil + + @searcher.find(@request).should be_nil + end + + it "should determine the file format from the file extension" do + @searcher.file_format("/data2.pson").should == "pson" + end + + it "should fail if the model does not support the file format" do + @searcher.stubs(:latest_path).returns "/my/file.pson" + + @model.expects(:support_format?).with("pson").returns false + + lambda { @searcher.find(@request) }.should raise_error(ArgumentError) + end + end + + describe "when saving files" do + before do + @content = "my content" + @file = stub 'file', :content => @content, :path => @path, :name => @path, :render => "mydata" + @request.stubs(:instance).returns @file + end + + it "should provide a method to save file contents at a specified path" do + @searcher.should respond_to(:save) + end + + it "should choose the file extension based on the default format of the model" do + @model.expects(:default_format).returns "pson" + + @searcher.serialization_format.should == "pson" + end + + it "should place the file in the data directory, named after the indirection, key, and format" do + @searcher.stubs(:data_directory).returns "/my/dir" + @searcher.stubs(:serialization_format).returns "pson" + + @request.stubs(:key).returns "foo" + @searcher.file_path(@request).should == File.join("/my/dir", "foo.pson") + end + + it "should fail intelligently if the file's parent directory does not exist" do + @searcher.stubs(:file_path).returns "/my/dir/file.pson" + @searcher.stubs(:serialization_format).returns "pson" + + @request.stubs(:key).returns "foo" + File.expects(:directory?).with(File.join("/my/dir")).returns(false) + + proc { @searcher.save(@request) }.should raise_error(Puppet::Error) + end + + it "should render the instance using the file format and print it to the file path" do + @searcher.stubs(:file_path).returns "/my/file.pson" + @searcher.stubs(:serialization_format).returns "pson" + + File.stubs(:directory?).returns true + + @request.instance.expects(:render).with("pson").returns "data" + + fh = mock 'filehandle' + File.expects(:open).with("/my/file.pson", "w").yields fh + fh.expects(:print).with("data") + + @searcher.save(@request) + end + + it "should fail intelligently if a file cannot be written" do + filehandle = mock 'file' + File.stubs(:directory?).returns(true) + File.stubs(:open).yields(filehandle) + filehandle.expects(:print).raises(ArgumentError) + + @searcher.stubs(:file_path).returns "/my/file.pson" + @model.stubs(:default_format).returns "pson" + + @instance.stubs(:render).returns "stuff" + + proc { @searcher.save(@request) }.should raise_error(Puppet::Error) + end + end + + describe "when removing files" do + it "should provide a method to remove files" do + @searcher.should respond_to(:destroy) + end + + it "should remove files in all formats found in the data directory that match the request key" do + @searcher.stubs(:data_directory).returns "/my/dir" + @request.stubs(:key).returns "me" + + Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns %w{/one /two} + + File.expects(:unlink).with("/one") + File.expects(:unlink).with("/two") + + @searcher.destroy(@request) + end + + it "should throw an exception if no file is found" do + @searcher.stubs(:data_directory).returns "/my/dir" + @request.stubs(:key).returns "me" + + Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns [] + + proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) + end + + it "should fail intelligently if a file cannot be removed" do + @searcher.stubs(:data_directory).returns "/my/dir" + @request.stubs(:key).returns "me" + + Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns %w{/one} + + File.expects(:unlink).with("/one").raises ArgumentError + + proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) + end + end +end diff --git a/spec/unit/indirector/file_spec_spec.rb b/spec/unit/indirector/file_spec_spec.rb deleted file mode 100755 index 207d897bf..000000000 --- a/spec/unit/indirector/file_spec_spec.rb +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector/file' - - -describe Puppet::Indirector::File do - before :each do - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @model = mock 'model' - @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model - Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - - @file_class = Class.new(Puppet::Indirector::File) do - def self.to_s - "Testing::Mytype" - end - end - - @searcher = @file_class.new - - @path = "/my/file" - @dir = "/my" - - @request = stub 'request', :key => @path - end - - describe "when finding files" do - it "should provide a method to return file contents at a specified path" do - @searcher.should respond_to(:find) - end - - it "should use the server data directory plus the indirection name if the process mode is master" do - Puppet.mode.expects(:master?).returns true - Puppet.settings.expects(:value).with(:server_datadir).returns "/my/dir" - - @searcher.data_directory.should == File.join("/my/dir", "mystuff") - end - - it "should use the client data directory plus the indirection name if the process mode is not master" do - Puppet.mode.expects(:master?).returns false - Puppet.settings.expects(:value).with(:client_datadir).returns "/my/dir" - - @searcher.data_directory.should == File.join("/my/dir", "mystuff") - end - - it "should use the newest file in the data directory matching the indirection key without extension" do - @searcher.expects(:data_directory).returns "/data/dir" - @request.stubs(:key).returns "foo" - Dir.expects(:glob).with("/data/dir/foo.*").returns %w{/data1.stuff /data2.stuff} - - stat1 = stub 'data1', :mtime => (Time.now - 5) - stat2 = stub 'data2', :mtime => Time.now - File.expects(:stat).with("/data1.stuff").returns stat1 - File.expects(:stat).with("/data2.stuff").returns stat2 - - @searcher.latest_path(@request).should == "/data2.stuff" - end - - it "should return nil when no files are found" do - @searcher.stubs(:latest_path).returns nil - - @searcher.find(@request).should be_nil - end - - it "should determine the file format from the file extension" do - @searcher.file_format("/data2.pson").should == "pson" - end - - it "should fail if the model does not support the file format" do - @searcher.stubs(:latest_path).returns "/my/file.pson" - - @model.expects(:support_format?).with("pson").returns false - - lambda { @searcher.find(@request) }.should raise_error(ArgumentError) - end - end - - describe "when saving files" do - before do - @content = "my content" - @file = stub 'file', :content => @content, :path => @path, :name => @path, :render => "mydata" - @request.stubs(:instance).returns @file - end - - it "should provide a method to save file contents at a specified path" do - @searcher.should respond_to(:save) - end - - it "should choose the file extension based on the default format of the model" do - @model.expects(:default_format).returns "pson" - - @searcher.serialization_format.should == "pson" - end - - it "should place the file in the data directory, named after the indirection, key, and format" do - @searcher.stubs(:data_directory).returns "/my/dir" - @searcher.stubs(:serialization_format).returns "pson" - - @request.stubs(:key).returns "foo" - @searcher.file_path(@request).should == File.join("/my/dir", "foo.pson") - end - - it "should fail intelligently if the file's parent directory does not exist" do - @searcher.stubs(:file_path).returns "/my/dir/file.pson" - @searcher.stubs(:serialization_format).returns "pson" - - @request.stubs(:key).returns "foo" - File.expects(:directory?).with(File.join("/my/dir")).returns(false) - - proc { @searcher.save(@request) }.should raise_error(Puppet::Error) - end - - it "should render the instance using the file format and print it to the file path" do - @searcher.stubs(:file_path).returns "/my/file.pson" - @searcher.stubs(:serialization_format).returns "pson" - - File.stubs(:directory?).returns true - - @request.instance.expects(:render).with("pson").returns "data" - - fh = mock 'filehandle' - File.expects(:open).with("/my/file.pson", "w").yields fh - fh.expects(:print).with("data") - - @searcher.save(@request) - end - - it "should fail intelligently if a file cannot be written" do - filehandle = mock 'file' - File.stubs(:directory?).returns(true) - File.stubs(:open).yields(filehandle) - filehandle.expects(:print).raises(ArgumentError) - - @searcher.stubs(:file_path).returns "/my/file.pson" - @model.stubs(:default_format).returns "pson" - - @instance.stubs(:render).returns "stuff" - - proc { @searcher.save(@request) }.should raise_error(Puppet::Error) - end - end - - describe "when removing files" do - it "should provide a method to remove files" do - @searcher.should respond_to(:destroy) - end - - it "should remove files in all formats found in the data directory that match the request key" do - @searcher.stubs(:data_directory).returns "/my/dir" - @request.stubs(:key).returns "me" - - Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns %w{/one /two} - - File.expects(:unlink).with("/one") - File.expects(:unlink).with("/two") - - @searcher.destroy(@request) - end - - it "should throw an exception if no file is found" do - @searcher.stubs(:data_directory).returns "/my/dir" - @request.stubs(:key).returns "me" - - Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns [] - - proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) - end - - it "should fail intelligently if a file cannot be removed" do - @searcher.stubs(:data_directory).returns "/my/dir" - @request.stubs(:key).returns "me" - - Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns %w{/one} - - File.expects(:unlink).with("/one").raises ArgumentError - - proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) - end - end -end diff --git a/spec/unit/indirector/indirection_spec.rb b/spec/unit/indirector/indirection_spec.rb new file mode 100755 index 000000000..8e62e1525 --- /dev/null +++ b/spec/unit/indirector/indirection_spec.rb @@ -0,0 +1,795 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/indirector/indirection' + +describe "Indirection Delegator", :shared => true do + it "should create a request object with the appropriate method name and all of the passed arguments" do + request = Puppet::Indirector::Request.new(:indirection, :find, "me") + + @indirection.expects(:request).with(@method, "mystuff", :one => :two).returns request + + @terminus.stubs(@method) + + @indirection.send(@method, "mystuff", :one => :two) + end + + it "should let the :select_terminus method choose the terminus using the created request if the :select_terminus method is available" do + # Define the method, so our respond_to? hook matches. + class << @indirection + def select_terminus(request) + end + end + + request = Puppet::Indirector::Request.new(:indirection, :find, "me") + + @indirection.stubs(:request).returns request + + @indirection.expects(:select_terminus).with(request).returns :test_terminus + + @indirection.stubs(:check_authorization) + @terminus.expects(@method) + + @indirection.send(@method, "me") + end + + it "should fail if the :select_terminus hook does not return a terminus name" do + # Define the method, so our respond_to? hook matches. + class << @indirection + def select_terminus(request) + end + end + + request = stub 'request', :key => "me", :options => {} + + @indirection.stubs(:request).returns request + + @indirection.expects(:select_terminus).with(request).returns nil + + lambda { @indirection.send(@method, "me") }.should raise_error(ArgumentError) + end + + it "should choose the terminus returned by the :terminus_class method if no :select_terminus method is available" do + @indirection.expects(:terminus_class).returns :test_terminus + + @terminus.expects(@method) + + @indirection.send(@method, "me") + end + + it "should let the appropriate terminus perform the lookup" do + @terminus.expects(@method).with { |r| r.is_a?(Puppet::Indirector::Request) } + @indirection.send(@method, "me") + end +end + +describe "Delegation Authorizer", :shared => true do + before do + # So the :respond_to? turns out correctly. + class << @terminus + def authorized? + end + end + end + + it "should not check authorization if a node name is not provided" do + @terminus.expects(:authorized?).never + @terminus.stubs(@method) + + # The quotes are necessary here, else it looks like a block. + @request.stubs(:options).returns({}) + @indirection.send(@method, "/my/key") + end + + it "should pass the request to the terminus's authorization method" do + @terminus.expects(:authorized?).with { |r| r.is_a?(Puppet::Indirector::Request) }.returns(true) + @terminus.stubs(@method) + + @indirection.send(@method, "/my/key", :node => "mynode") + end + + it "should fail if authorization returns false" do + @terminus.expects(:authorized?).returns(false) + @terminus.stubs(@method) + proc { @indirection.send(@method, "/my/key", :node => "mynode") }.should raise_error(ArgumentError) + end + + it "should continue if authorization returns true" do + @terminus.expects(:authorized?).returns(true) + @terminus.stubs(@method) + @indirection.send(@method, "/my/key", :node => "mynode") + end +end + +describe Puppet::Indirector::Indirection do + after do + Puppet::Util::Cacher.expire + end + describe "when initializing" do + # (LAK) I've no idea how to test this, really. + it "should store a reference to itself before it consumes its options" do + proc { @indirection = Puppet::Indirector::Indirection.new(Object.new, :testingness, :not_valid_option) }.should raise_error + Puppet::Indirector::Indirection.instance(:testingness).should be_instance_of(Puppet::Indirector::Indirection) + Puppet::Indirector::Indirection.instance(:testingness).delete + end + + it "should keep a reference to the indirecting model" do + model = mock 'model' + @indirection = Puppet::Indirector::Indirection.new(model, :myind) + @indirection.model.should equal(model) + end + + it "should set the name" do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind) + @indirection.name.should == :myind + end + + it "should require indirections to have unique names" do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + proc { Puppet::Indirector::Indirection.new(:test) }.should raise_error(ArgumentError) + end + + it "should extend itself with any specified module" do + mod = Module.new + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :extend => mod) + @indirection.singleton_class.included_modules.should include(mod) + end + + after do + @indirection.delete if defined? @indirection + end + end + + describe "when an instance" do + before :each do + @terminus_class = mock 'terminus_class' + @terminus = mock 'terminus' + @terminus_class.stubs(:new).returns(@terminus) + @cache = stub 'cache', :name => "mycache" + @cache_class = mock 'cache_class' + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) + + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @indirection.terminus_class = :test_terminus + + @instance = stub 'instance', :expiration => nil, :expiration= => nil, :name => "whatever" + @name = :mything + + #@request = stub 'instance', :key => "/my/key", :instance => @instance, :options => {} + @request = mock 'instance' + end + + it "should allow setting the ttl" do + @indirection.ttl = 300 + @indirection.ttl.should == 300 + end + + it "should default to the :runinterval setting, converted to an integer, for its ttl" do + Puppet.settings.expects(:value).returns "1800" + @indirection.ttl.should == 1800 + end + + it "should calculate the current expiration by adding the TTL to the current time" do + @indirection.stubs(:ttl).returns(100) + now = Time.now + Time.stubs(:now).returns now + @indirection.expiration.should == (Time.now + 100) + end + + it "should have a method for creating an indirection request instance" do + @indirection.should respond_to(:request) + end + + describe "creates a request" do + it "should create it with its name as the request's indirection name" do + Puppet::Indirector::Request.expects(:new).with { |name, *other| @indirection.name == name } + @indirection.request(:funtest, "yayness") + end + + it "should require a method and key" do + Puppet::Indirector::Request.expects(:new).with { |name, method, key, *other| method == :funtest and key == "yayness" } + @indirection.request(:funtest, "yayness") + end + + it "should support optional arguments" do + Puppet::Indirector::Request.expects(:new).with { |name, method, key, other| other == {:one => :two} } + @indirection.request(:funtest, "yayness", :one => :two) + end + + it "should not pass options if none are supplied" do + Puppet::Indirector::Request.expects(:new).with { |*args| args.length < 4 } + @indirection.request(:funtest, "yayness") + end + + it "should return the request" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).returns request + @indirection.request(:funtest, "yayness").should equal(request) + end + end + + describe "and looking for a model instance" do + before { @method = :find } + + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" + + it "should return the results of the delegation" do + @terminus.expects(:find).returns(@instance) + @indirection.find("me").should equal(@instance) + end + + it "should set the expiration date on any instances without one set" do + @terminus.stubs(:find).returns(@instance) + + @indirection.expects(:expiration).returns :yay + + @instance.expects(:expiration).returns(nil) + @instance.expects(:expiration=).with(:yay) + + @indirection.find("/my/key") + end + + it "should not override an already-set expiration date on returned instances" do + @terminus.stubs(:find).returns(@instance) + + @indirection.expects(:expiration).never + + @instance.expects(:expiration).returns(:yay) + @instance.expects(:expiration=).never + + @indirection.find("/my/key") + end + + it "should filter the result instance if the terminus supports it" do + @terminus.stubs(:find).returns(@instance) + @terminus.stubs(:respond_to?).with(:filter).returns(true) + + @terminus.expects(:filter).with(@instance) + + @indirection.find("/my/key") + end + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.stubs(:new).returns(@cache) + + @instance.stubs(:expired?).returns false + end + + it "should first look in the cache for an instance" do + @terminus.stubs(:find).never + @cache.expects(:find).returns @instance + + @indirection.find("/my/key") + end + + it "should not look in the cache if the request specifies not to use the cache" do + @terminus.expects(:find).returns @instance + @cache.expects(:find).never + @cache.stubs(:save) + + @indirection.find("/my/key", :ignore_cache => true) + end + + it "should still save to the cache even if the cache is being ignored during readin" do + @terminus.expects(:find).returns @instance + @cache.expects(:save) + + @indirection.find("/my/key", :ignore_cache => true) + end + + it "should only look in the cache if the request specifies not to use the terminus" do + @terminus.expects(:find).never + @cache.expects(:find) + + @indirection.find("/my/key", :ignore_terminus => true) + end + + it "should use a request to look in the cache for cached objects" do + @cache.expects(:find).with { |r| r.method == :find and r.key == "/my/key" }.returns @instance + + @cache.stubs(:save) + + @indirection.find("/my/key") + end + + it "should return the cached object if it is not expired" do + @instance.stubs(:expired?).returns false + + @cache.stubs(:find).returns @instance + @indirection.find("/my/key").should equal(@instance) + end + + it "should not fail if the cache fails" do + @terminus.stubs(:find).returns @instance + + @cache.expects(:find).raises ArgumentError + @cache.stubs(:save) + lambda { @indirection.find("/my/key") }.should_not raise_error + end + + it "should look in the main terminus if the cache fails" do + @terminus.expects(:find).returns @instance + @cache.expects(:find).raises ArgumentError + @cache.stubs(:save) + @indirection.find("/my/key").should equal(@instance) + end + + it "should send a debug log if it is using the cached object" do + Puppet.expects(:debug) + @cache.stubs(:find).returns @instance + + @indirection.find("/my/key") + end + + it "should not return the cached object if it is expired" do + @instance.stubs(:expired?).returns true + + @cache.stubs(:find).returns @instance + @terminus.stubs(:find).returns nil + @indirection.find("/my/key").should be_nil + end + + it "should send an info log if it is using the cached object" do + Puppet.expects(:info) + @instance.stubs(:expired?).returns true + + @cache.stubs(:find).returns @instance + @terminus.stubs(:find).returns nil + @indirection.find("/my/key") + end + + it "should cache any objects not retrieved from the cache" do + @cache.expects(:find).returns nil + + @terminus.expects(:find).returns(@instance) + @cache.expects(:save) + + @indirection.find("/my/key") + end + + it "should use a request to look in the cache for cached objects" do + @cache.expects(:find).with { |r| r.method == :find and r.key == "/my/key" }.returns nil + + @terminus.stubs(:find).returns(@instance) + @cache.stubs(:save) + + @indirection.find("/my/key") + end + + it "should cache the instance using a request with the instance set to the cached object" do + @cache.stubs(:find).returns nil + + @terminus.stubs(:find).returns(@instance) + + @cache.expects(:save).with { |r| r.method == :save and r.instance == @instance } + + @indirection.find("/my/key") + end + + it "should send an info log that the object is being cached" do + @cache.stubs(:find).returns nil + + @terminus.stubs(:find).returns(@instance) + @cache.stubs(:save) + + Puppet.expects(:info) + + @indirection.find("/my/key") + end + end + end + + describe "and storing a model instance" do + before { @method = :save } + + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" + + it "should return the result of the save" do + @terminus.stubs(:save).returns "foo" + @indirection.save(@instance).should == "foo" + end + + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.stubs(:new).returns(@cache) + + @instance.stubs(:expired?).returns false + end + + it "should return the result of saving to the terminus" do + request = stub 'request', :instance => @instance, :node => nil + + @indirection.expects(:request).returns request + + @cache.stubs(:save) + @terminus.stubs(:save).returns @instance + @indirection.save(@instance).should equal(@instance) + end + + it "should use a request to save the object to the cache" do + request = stub 'request', :instance => @instance, :node => nil + + @indirection.expects(:request).returns request + + @cache.expects(:save).with(request) + @terminus.stubs(:save) + @indirection.save(@instance) + end + + it "should not save to the cache if the normal save fails" do + request = stub 'request', :instance => @instance, :node => nil + + @indirection.expects(:request).returns request + + @cache.expects(:save).never + @terminus.expects(:save).raises "eh" + lambda { @indirection.save(@instance) }.should raise_error + end + end + end + + describe "and removing a model instance" do + before { @method = :destroy } + + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" + + it "should return the result of removing the instance" do + @terminus.stubs(:destroy).returns "yayness" + @indirection.destroy("/my/key").should == "yayness" + end + + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) + + @instance.stubs(:expired?).returns false + end + + it "should use a request instance to search in and remove objects from the cache" do + destroy = stub 'destroy_request', :key => "/my/key", :node => nil + find = stub 'destroy_request', :key => "/my/key", :node => nil + + @indirection.expects(:request).with(:destroy, "/my/key").returns destroy + @indirection.expects(:request).with(:find, "/my/key").returns find + + cached = mock 'cache' + + @cache.expects(:find).with(find).returns cached + @cache.expects(:destroy).with(destroy) + + @terminus.stubs(:destroy) + + @indirection.destroy("/my/key") + end + end + end + + describe "and searching for multiple model instances" do + before { @method = :search } + + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" + + it "should set the expiration date on any instances without one set" do + @terminus.stubs(:search).returns([@instance]) + + @indirection.expects(:expiration).returns :yay + + @instance.expects(:expiration).returns(nil) + @instance.expects(:expiration=).with(:yay) + + @indirection.search("/my/key") + end + + it "should not override an already-set expiration date on returned instances" do + @terminus.stubs(:search).returns([@instance]) + + @indirection.expects(:expiration).never + + @instance.expects(:expiration).returns(:yay) + @instance.expects(:expiration=).never + + @indirection.search("/my/key") + end + + it "should return the results of searching in the terminus" do + @terminus.expects(:search).returns([@instance]) + @indirection.search("/my/key").should == [@instance] + end + end + + describe "and expiring a model instance" do + describe "when caching is not enabled" do + it "should do nothing" do + @cache_class.expects(:new).never + + @indirection.expire("/my/key") + end + end + + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) + + @instance.stubs(:expired?).returns false + + @cached = stub 'cached', :expiration= => nil, :name => "/my/key" + end + + it "should use a request to find within the cache" do + @cache.expects(:find).with { |r| r.is_a?(Puppet::Indirector::Request) and r.method == :find } + @indirection.expire("/my/key") + end + + it "should do nothing if no such instance is cached" do + @cache.expects(:find).returns nil + + @indirection.expire("/my/key") + end + + it "should log when expiring a found instance" do + @cache.expects(:find).returns @cached + @cache.stubs(:save) + + Puppet.expects(:info) + + @indirection.expire("/my/key") + end + + it "should set the cached instance's expiration to a time in the past" do + @cache.expects(:find).returns @cached + @cache.stubs(:save) + + @cached.expects(:expiration=).with { |t| t < Time.now } + + @indirection.expire("/my/key") + end + + it "should save the now expired instance back into the cache" do + @cache.expects(:find).returns @cached + + @cached.expects(:expiration=).with { |t| t < Time.now } + + @cache.expects(:save) + + @indirection.expire("/my/key") + end + + it "should use a request to save the expired resource to the cache" do + @cache.expects(:find).returns @cached + + @cached.expects(:expiration=).with { |t| t < Time.now } + + @cache.expects(:save).with { |r| r.is_a?(Puppet::Indirector::Request) and r.instance == @cached and r.method == :save }.returns(@cached) + + @indirection.expire("/my/key") + end + end + end + + after :each do + @indirection.delete + Puppet::Util::Cacher.expire + end + end + + + describe "when managing indirection instances" do + it "should allow an indirection to be retrieved by name" do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + Puppet::Indirector::Indirection.instance(:test).should equal(@indirection) + end + + it "should return nil when the named indirection has not been created" do + Puppet::Indirector::Indirection.instance(:test).should be_nil + end + + it "should allow an indirection's model to be retrieved by name" do + mock_model = mock('model') + @indirection = Puppet::Indirector::Indirection.new(mock_model, :test) + Puppet::Indirector::Indirection.model(:test).should equal(mock_model) + end + + it "should return nil when no model matches the requested name" do + Puppet::Indirector::Indirection.model(:test).should be_nil + end + + after do + @indirection.delete if defined? @indirection + end + end + + describe "when routing to the correct the terminus class" do + before do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @terminus = mock 'terminus' + @terminus_class = stub 'terminus class', :new => @terminus + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :default).returns(@terminus_class) + end + + it "should fail if no terminus class can be picked" do + proc { @indirection.terminus_class }.should raise_error(Puppet::DevError) + end + + it "should choose the default terminus class if one is specified" do + @indirection.terminus_class = :default + @indirection.terminus_class.should equal(:default) + end + + it "should use the provided Puppet setting if told to do so" do + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :my_terminus).returns(mock("terminus_class2")) + Puppet.settings.expects(:value).with(:my_setting).returns("my_terminus") + @indirection.terminus_setting = :my_setting + @indirection.terminus_class.should equal(:my_terminus) + end + + it "should fail if the provided terminus class is not valid" do + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :nosuchclass).returns(nil) + proc { @indirection.terminus_class = :nosuchclass }.should raise_error(ArgumentError) + end + + after do + @indirection.delete if defined? @indirection + end + end + + describe "when specifying the terminus class to use" do + before do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @terminus = mock 'terminus' + @terminus_class = stub 'terminus class', :new => @terminus + end + + it "should allow specification of a terminus type" do + @indirection.should respond_to(:terminus_class=) + end + + it "should fail to redirect if no terminus type has been specified" do + proc { @indirection.find("blah") }.should raise_error(Puppet::DevError) + end + + it "should fail when the terminus class name is an empty string" do + proc { @indirection.terminus_class = "" }.should raise_error(ArgumentError) + end + + it "should fail when the terminus class name is nil" do + proc { @indirection.terminus_class = nil }.should raise_error(ArgumentError) + end + + it "should fail when the specified terminus class cannot be found" do + Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) + proc { @indirection.terminus_class = :foo }.should raise_error(ArgumentError) + end + + it "should select the specified terminus class if a terminus class name is provided" do + Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(@terminus_class) + @indirection.terminus(:foo).should equal(@terminus) + end + + it "should use the configured terminus class if no terminus name is specified" do + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) + @indirection.terminus_class = :foo + @indirection.terminus().should equal(@terminus) + end + + after do + @indirection.delete if defined? @indirection + end + end + + describe "when managing terminus instances" do + before do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @terminus = mock 'terminus' + @terminus_class = mock 'terminus class' + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) + end + + it "should create an instance of the chosen terminus class" do + @terminus_class.stubs(:new).returns(@terminus) + @indirection.terminus(:foo).should equal(@terminus) + end + + # Make sure it caches the terminus. + it "should return the same terminus instance each time for a given name" do + @terminus_class.stubs(:new).returns(@terminus) + @indirection.terminus(:foo).should equal(@terminus) + @indirection.terminus(:foo).should equal(@terminus) + end + + it "should not create a terminus instance until one is actually needed" do + Puppet::Indirector.expects(:terminus).never + indirection = Puppet::Indirector::Indirection.new(mock('model'), :lazytest) + end + + after do + @indirection.delete + end + end + + describe "when deciding whether to cache" do + before do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @terminus = mock 'terminus' + @terminus_class = mock 'terminus class' + @terminus_class.stubs(:new).returns(@terminus) + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) + @indirection.terminus_class = :foo + end + + it "should provide a method for setting the cache terminus class" do + @indirection.should respond_to(:cache_class=) + end + + it "should fail to cache if no cache type has been specified" do + proc { @indirection.cache }.should raise_error(Puppet::DevError) + end + + it "should fail to set the cache class when the cache class name is an empty string" do + proc { @indirection.cache_class = "" }.should raise_error(ArgumentError) + end + + it "should allow resetting the cache_class to nil" do + @indirection.cache_class = nil + @indirection.cache_class.should be_nil + end + + it "should fail to set the cache class when the specified cache class cannot be found" do + Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) + proc { @indirection.cache_class = :foo }.should raise_error(ArgumentError) + end + + after do + @indirection.delete + end + end + + describe "when using a cache" do + before :each do + Puppet.settings.stubs(:value).with("test_terminus").returns("test_terminus") + @terminus_class = mock 'terminus_class' + @terminus = mock 'terminus' + @terminus_class.stubs(:new).returns(@terminus) + @cache = mock 'cache' + @cache_class = mock 'cache_class' + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @indirection.terminus_class = :test_terminus + end + + describe "and managing the cache terminus" do + it "should not create a cache terminus at initialization" do + # This is weird, because all of the code is in the setup. If we got + # new() called on the cache class, we'd get an exception here. + end + + it "should reuse the cache terminus" do + @cache_class.expects(:new).returns(@cache) + Puppet.settings.stubs(:value).with("test_cache").returns("cache_terminus") + @indirection.cache_class = :cache_terminus + @indirection.cache.should equal(@cache) + @indirection.cache.should equal(@cache) + end + end + + describe "and saving" do + end + + describe "and finding" do + end + + after :each do + @indirection.delete + end + end +end diff --git a/spec/unit/indirector/indirection_spec_spec.rb b/spec/unit/indirector/indirection_spec_spec.rb deleted file mode 100755 index 8e62e1525..000000000 --- a/spec/unit/indirector/indirection_spec_spec.rb +++ /dev/null @@ -1,795 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/indirector/indirection' - -describe "Indirection Delegator", :shared => true do - it "should create a request object with the appropriate method name and all of the passed arguments" do - request = Puppet::Indirector::Request.new(:indirection, :find, "me") - - @indirection.expects(:request).with(@method, "mystuff", :one => :two).returns request - - @terminus.stubs(@method) - - @indirection.send(@method, "mystuff", :one => :two) - end - - it "should let the :select_terminus method choose the terminus using the created request if the :select_terminus method is available" do - # Define the method, so our respond_to? hook matches. - class << @indirection - def select_terminus(request) - end - end - - request = Puppet::Indirector::Request.new(:indirection, :find, "me") - - @indirection.stubs(:request).returns request - - @indirection.expects(:select_terminus).with(request).returns :test_terminus - - @indirection.stubs(:check_authorization) - @terminus.expects(@method) - - @indirection.send(@method, "me") - end - - it "should fail if the :select_terminus hook does not return a terminus name" do - # Define the method, so our respond_to? hook matches. - class << @indirection - def select_terminus(request) - end - end - - request = stub 'request', :key => "me", :options => {} - - @indirection.stubs(:request).returns request - - @indirection.expects(:select_terminus).with(request).returns nil - - lambda { @indirection.send(@method, "me") }.should raise_error(ArgumentError) - end - - it "should choose the terminus returned by the :terminus_class method if no :select_terminus method is available" do - @indirection.expects(:terminus_class).returns :test_terminus - - @terminus.expects(@method) - - @indirection.send(@method, "me") - end - - it "should let the appropriate terminus perform the lookup" do - @terminus.expects(@method).with { |r| r.is_a?(Puppet::Indirector::Request) } - @indirection.send(@method, "me") - end -end - -describe "Delegation Authorizer", :shared => true do - before do - # So the :respond_to? turns out correctly. - class << @terminus - def authorized? - end - end - end - - it "should not check authorization if a node name is not provided" do - @terminus.expects(:authorized?).never - @terminus.stubs(@method) - - # The quotes are necessary here, else it looks like a block. - @request.stubs(:options).returns({}) - @indirection.send(@method, "/my/key") - end - - it "should pass the request to the terminus's authorization method" do - @terminus.expects(:authorized?).with { |r| r.is_a?(Puppet::Indirector::Request) }.returns(true) - @terminus.stubs(@method) - - @indirection.send(@method, "/my/key", :node => "mynode") - end - - it "should fail if authorization returns false" do - @terminus.expects(:authorized?).returns(false) - @terminus.stubs(@method) - proc { @indirection.send(@method, "/my/key", :node => "mynode") }.should raise_error(ArgumentError) - end - - it "should continue if authorization returns true" do - @terminus.expects(:authorized?).returns(true) - @terminus.stubs(@method) - @indirection.send(@method, "/my/key", :node => "mynode") - end -end - -describe Puppet::Indirector::Indirection do - after do - Puppet::Util::Cacher.expire - end - describe "when initializing" do - # (LAK) I've no idea how to test this, really. - it "should store a reference to itself before it consumes its options" do - proc { @indirection = Puppet::Indirector::Indirection.new(Object.new, :testingness, :not_valid_option) }.should raise_error - Puppet::Indirector::Indirection.instance(:testingness).should be_instance_of(Puppet::Indirector::Indirection) - Puppet::Indirector::Indirection.instance(:testingness).delete - end - - it "should keep a reference to the indirecting model" do - model = mock 'model' - @indirection = Puppet::Indirector::Indirection.new(model, :myind) - @indirection.model.should equal(model) - end - - it "should set the name" do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind) - @indirection.name.should == :myind - end - - it "should require indirections to have unique names" do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - proc { Puppet::Indirector::Indirection.new(:test) }.should raise_error(ArgumentError) - end - - it "should extend itself with any specified module" do - mod = Module.new - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :extend => mod) - @indirection.singleton_class.included_modules.should include(mod) - end - - after do - @indirection.delete if defined? @indirection - end - end - - describe "when an instance" do - before :each do - @terminus_class = mock 'terminus_class' - @terminus = mock 'terminus' - @terminus_class.stubs(:new).returns(@terminus) - @cache = stub 'cache', :name => "mycache" - @cache_class = mock 'cache_class' - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) - - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @indirection.terminus_class = :test_terminus - - @instance = stub 'instance', :expiration => nil, :expiration= => nil, :name => "whatever" - @name = :mything - - #@request = stub 'instance', :key => "/my/key", :instance => @instance, :options => {} - @request = mock 'instance' - end - - it "should allow setting the ttl" do - @indirection.ttl = 300 - @indirection.ttl.should == 300 - end - - it "should default to the :runinterval setting, converted to an integer, for its ttl" do - Puppet.settings.expects(:value).returns "1800" - @indirection.ttl.should == 1800 - end - - it "should calculate the current expiration by adding the TTL to the current time" do - @indirection.stubs(:ttl).returns(100) - now = Time.now - Time.stubs(:now).returns now - @indirection.expiration.should == (Time.now + 100) - end - - it "should have a method for creating an indirection request instance" do - @indirection.should respond_to(:request) - end - - describe "creates a request" do - it "should create it with its name as the request's indirection name" do - Puppet::Indirector::Request.expects(:new).with { |name, *other| @indirection.name == name } - @indirection.request(:funtest, "yayness") - end - - it "should require a method and key" do - Puppet::Indirector::Request.expects(:new).with { |name, method, key, *other| method == :funtest and key == "yayness" } - @indirection.request(:funtest, "yayness") - end - - it "should support optional arguments" do - Puppet::Indirector::Request.expects(:new).with { |name, method, key, other| other == {:one => :two} } - @indirection.request(:funtest, "yayness", :one => :two) - end - - it "should not pass options if none are supplied" do - Puppet::Indirector::Request.expects(:new).with { |*args| args.length < 4 } - @indirection.request(:funtest, "yayness") - end - - it "should return the request" do - request = mock 'request' - Puppet::Indirector::Request.expects(:new).returns request - @indirection.request(:funtest, "yayness").should equal(request) - end - end - - describe "and looking for a model instance" do - before { @method = :find } - - it_should_behave_like "Indirection Delegator" - it_should_behave_like "Delegation Authorizer" - - it "should return the results of the delegation" do - @terminus.expects(:find).returns(@instance) - @indirection.find("me").should equal(@instance) - end - - it "should set the expiration date on any instances without one set" do - @terminus.stubs(:find).returns(@instance) - - @indirection.expects(:expiration).returns :yay - - @instance.expects(:expiration).returns(nil) - @instance.expects(:expiration=).with(:yay) - - @indirection.find("/my/key") - end - - it "should not override an already-set expiration date on returned instances" do - @terminus.stubs(:find).returns(@instance) - - @indirection.expects(:expiration).never - - @instance.expects(:expiration).returns(:yay) - @instance.expects(:expiration=).never - - @indirection.find("/my/key") - end - - it "should filter the result instance if the terminus supports it" do - @terminus.stubs(:find).returns(@instance) - @terminus.stubs(:respond_to?).with(:filter).returns(true) - - @terminus.expects(:filter).with(@instance) - - @indirection.find("/my/key") - end - describe "when caching is enabled" do - before do - @indirection.cache_class = :cache_terminus - @cache_class.stubs(:new).returns(@cache) - - @instance.stubs(:expired?).returns false - end - - it "should first look in the cache for an instance" do - @terminus.stubs(:find).never - @cache.expects(:find).returns @instance - - @indirection.find("/my/key") - end - - it "should not look in the cache if the request specifies not to use the cache" do - @terminus.expects(:find).returns @instance - @cache.expects(:find).never - @cache.stubs(:save) - - @indirection.find("/my/key", :ignore_cache => true) - end - - it "should still save to the cache even if the cache is being ignored during readin" do - @terminus.expects(:find).returns @instance - @cache.expects(:save) - - @indirection.find("/my/key", :ignore_cache => true) - end - - it "should only look in the cache if the request specifies not to use the terminus" do - @terminus.expects(:find).never - @cache.expects(:find) - - @indirection.find("/my/key", :ignore_terminus => true) - end - - it "should use a request to look in the cache for cached objects" do - @cache.expects(:find).with { |r| r.method == :find and r.key == "/my/key" }.returns @instance - - @cache.stubs(:save) - - @indirection.find("/my/key") - end - - it "should return the cached object if it is not expired" do - @instance.stubs(:expired?).returns false - - @cache.stubs(:find).returns @instance - @indirection.find("/my/key").should equal(@instance) - end - - it "should not fail if the cache fails" do - @terminus.stubs(:find).returns @instance - - @cache.expects(:find).raises ArgumentError - @cache.stubs(:save) - lambda { @indirection.find("/my/key") }.should_not raise_error - end - - it "should look in the main terminus if the cache fails" do - @terminus.expects(:find).returns @instance - @cache.expects(:find).raises ArgumentError - @cache.stubs(:save) - @indirection.find("/my/key").should equal(@instance) - end - - it "should send a debug log if it is using the cached object" do - Puppet.expects(:debug) - @cache.stubs(:find).returns @instance - - @indirection.find("/my/key") - end - - it "should not return the cached object if it is expired" do - @instance.stubs(:expired?).returns true - - @cache.stubs(:find).returns @instance - @terminus.stubs(:find).returns nil - @indirection.find("/my/key").should be_nil - end - - it "should send an info log if it is using the cached object" do - Puppet.expects(:info) - @instance.stubs(:expired?).returns true - - @cache.stubs(:find).returns @instance - @terminus.stubs(:find).returns nil - @indirection.find("/my/key") - end - - it "should cache any objects not retrieved from the cache" do - @cache.expects(:find).returns nil - - @terminus.expects(:find).returns(@instance) - @cache.expects(:save) - - @indirection.find("/my/key") - end - - it "should use a request to look in the cache for cached objects" do - @cache.expects(:find).with { |r| r.method == :find and r.key == "/my/key" }.returns nil - - @terminus.stubs(:find).returns(@instance) - @cache.stubs(:save) - - @indirection.find("/my/key") - end - - it "should cache the instance using a request with the instance set to the cached object" do - @cache.stubs(:find).returns nil - - @terminus.stubs(:find).returns(@instance) - - @cache.expects(:save).with { |r| r.method == :save and r.instance == @instance } - - @indirection.find("/my/key") - end - - it "should send an info log that the object is being cached" do - @cache.stubs(:find).returns nil - - @terminus.stubs(:find).returns(@instance) - @cache.stubs(:save) - - Puppet.expects(:info) - - @indirection.find("/my/key") - end - end - end - - describe "and storing a model instance" do - before { @method = :save } - - it_should_behave_like "Indirection Delegator" - it_should_behave_like "Delegation Authorizer" - - it "should return the result of the save" do - @terminus.stubs(:save).returns "foo" - @indirection.save(@instance).should == "foo" - end - - describe "when caching is enabled" do - before do - @indirection.cache_class = :cache_terminus - @cache_class.stubs(:new).returns(@cache) - - @instance.stubs(:expired?).returns false - end - - it "should return the result of saving to the terminus" do - request = stub 'request', :instance => @instance, :node => nil - - @indirection.expects(:request).returns request - - @cache.stubs(:save) - @terminus.stubs(:save).returns @instance - @indirection.save(@instance).should equal(@instance) - end - - it "should use a request to save the object to the cache" do - request = stub 'request', :instance => @instance, :node => nil - - @indirection.expects(:request).returns request - - @cache.expects(:save).with(request) - @terminus.stubs(:save) - @indirection.save(@instance) - end - - it "should not save to the cache if the normal save fails" do - request = stub 'request', :instance => @instance, :node => nil - - @indirection.expects(:request).returns request - - @cache.expects(:save).never - @terminus.expects(:save).raises "eh" - lambda { @indirection.save(@instance) }.should raise_error - end - end - end - - describe "and removing a model instance" do - before { @method = :destroy } - - it_should_behave_like "Indirection Delegator" - it_should_behave_like "Delegation Authorizer" - - it "should return the result of removing the instance" do - @terminus.stubs(:destroy).returns "yayness" - @indirection.destroy("/my/key").should == "yayness" - end - - describe "when caching is enabled" do - before do - @indirection.cache_class = :cache_terminus - @cache_class.expects(:new).returns(@cache) - - @instance.stubs(:expired?).returns false - end - - it "should use a request instance to search in and remove objects from the cache" do - destroy = stub 'destroy_request', :key => "/my/key", :node => nil - find = stub 'destroy_request', :key => "/my/key", :node => nil - - @indirection.expects(:request).with(:destroy, "/my/key").returns destroy - @indirection.expects(:request).with(:find, "/my/key").returns find - - cached = mock 'cache' - - @cache.expects(:find).with(find).returns cached - @cache.expects(:destroy).with(destroy) - - @terminus.stubs(:destroy) - - @indirection.destroy("/my/key") - end - end - end - - describe "and searching for multiple model instances" do - before { @method = :search } - - it_should_behave_like "Indirection Delegator" - it_should_behave_like "Delegation Authorizer" - - it "should set the expiration date on any instances without one set" do - @terminus.stubs(:search).returns([@instance]) - - @indirection.expects(:expiration).returns :yay - - @instance.expects(:expiration).returns(nil) - @instance.expects(:expiration=).with(:yay) - - @indirection.search("/my/key") - end - - it "should not override an already-set expiration date on returned instances" do - @terminus.stubs(:search).returns([@instance]) - - @indirection.expects(:expiration).never - - @instance.expects(:expiration).returns(:yay) - @instance.expects(:expiration=).never - - @indirection.search("/my/key") - end - - it "should return the results of searching in the terminus" do - @terminus.expects(:search).returns([@instance]) - @indirection.search("/my/key").should == [@instance] - end - end - - describe "and expiring a model instance" do - describe "when caching is not enabled" do - it "should do nothing" do - @cache_class.expects(:new).never - - @indirection.expire("/my/key") - end - end - - describe "when caching is enabled" do - before do - @indirection.cache_class = :cache_terminus - @cache_class.expects(:new).returns(@cache) - - @instance.stubs(:expired?).returns false - - @cached = stub 'cached', :expiration= => nil, :name => "/my/key" - end - - it "should use a request to find within the cache" do - @cache.expects(:find).with { |r| r.is_a?(Puppet::Indirector::Request) and r.method == :find } - @indirection.expire("/my/key") - end - - it "should do nothing if no such instance is cached" do - @cache.expects(:find).returns nil - - @indirection.expire("/my/key") - end - - it "should log when expiring a found instance" do - @cache.expects(:find).returns @cached - @cache.stubs(:save) - - Puppet.expects(:info) - - @indirection.expire("/my/key") - end - - it "should set the cached instance's expiration to a time in the past" do - @cache.expects(:find).returns @cached - @cache.stubs(:save) - - @cached.expects(:expiration=).with { |t| t < Time.now } - - @indirection.expire("/my/key") - end - - it "should save the now expired instance back into the cache" do - @cache.expects(:find).returns @cached - - @cached.expects(:expiration=).with { |t| t < Time.now } - - @cache.expects(:save) - - @indirection.expire("/my/key") - end - - it "should use a request to save the expired resource to the cache" do - @cache.expects(:find).returns @cached - - @cached.expects(:expiration=).with { |t| t < Time.now } - - @cache.expects(:save).with { |r| r.is_a?(Puppet::Indirector::Request) and r.instance == @cached and r.method == :save }.returns(@cached) - - @indirection.expire("/my/key") - end - end - end - - after :each do - @indirection.delete - Puppet::Util::Cacher.expire - end - end - - - describe "when managing indirection instances" do - it "should allow an indirection to be retrieved by name" do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - Puppet::Indirector::Indirection.instance(:test).should equal(@indirection) - end - - it "should return nil when the named indirection has not been created" do - Puppet::Indirector::Indirection.instance(:test).should be_nil - end - - it "should allow an indirection's model to be retrieved by name" do - mock_model = mock('model') - @indirection = Puppet::Indirector::Indirection.new(mock_model, :test) - Puppet::Indirector::Indirection.model(:test).should equal(mock_model) - end - - it "should return nil when no model matches the requested name" do - Puppet::Indirector::Indirection.model(:test).should be_nil - end - - after do - @indirection.delete if defined? @indirection - end - end - - describe "when routing to the correct the terminus class" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = mock 'terminus' - @terminus_class = stub 'terminus class', :new => @terminus - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :default).returns(@terminus_class) - end - - it "should fail if no terminus class can be picked" do - proc { @indirection.terminus_class }.should raise_error(Puppet::DevError) - end - - it "should choose the default terminus class if one is specified" do - @indirection.terminus_class = :default - @indirection.terminus_class.should equal(:default) - end - - it "should use the provided Puppet setting if told to do so" do - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :my_terminus).returns(mock("terminus_class2")) - Puppet.settings.expects(:value).with(:my_setting).returns("my_terminus") - @indirection.terminus_setting = :my_setting - @indirection.terminus_class.should equal(:my_terminus) - end - - it "should fail if the provided terminus class is not valid" do - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :nosuchclass).returns(nil) - proc { @indirection.terminus_class = :nosuchclass }.should raise_error(ArgumentError) - end - - after do - @indirection.delete if defined? @indirection - end - end - - describe "when specifying the terminus class to use" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = mock 'terminus' - @terminus_class = stub 'terminus class', :new => @terminus - end - - it "should allow specification of a terminus type" do - @indirection.should respond_to(:terminus_class=) - end - - it "should fail to redirect if no terminus type has been specified" do - proc { @indirection.find("blah") }.should raise_error(Puppet::DevError) - end - - it "should fail when the terminus class name is an empty string" do - proc { @indirection.terminus_class = "" }.should raise_error(ArgumentError) - end - - it "should fail when the terminus class name is nil" do - proc { @indirection.terminus_class = nil }.should raise_error(ArgumentError) - end - - it "should fail when the specified terminus class cannot be found" do - Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) - proc { @indirection.terminus_class = :foo }.should raise_error(ArgumentError) - end - - it "should select the specified terminus class if a terminus class name is provided" do - Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(@terminus_class) - @indirection.terminus(:foo).should equal(@terminus) - end - - it "should use the configured terminus class if no terminus name is specified" do - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) - @indirection.terminus_class = :foo - @indirection.terminus().should equal(@terminus) - end - - after do - @indirection.delete if defined? @indirection - end - end - - describe "when managing terminus instances" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = mock 'terminus' - @terminus_class = mock 'terminus class' - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) - end - - it "should create an instance of the chosen terminus class" do - @terminus_class.stubs(:new).returns(@terminus) - @indirection.terminus(:foo).should equal(@terminus) - end - - # Make sure it caches the terminus. - it "should return the same terminus instance each time for a given name" do - @terminus_class.stubs(:new).returns(@terminus) - @indirection.terminus(:foo).should equal(@terminus) - @indirection.terminus(:foo).should equal(@terminus) - end - - it "should not create a terminus instance until one is actually needed" do - Puppet::Indirector.expects(:terminus).never - indirection = Puppet::Indirector::Indirection.new(mock('model'), :lazytest) - end - - after do - @indirection.delete - end - end - - describe "when deciding whether to cache" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = mock 'terminus' - @terminus_class = mock 'terminus class' - @terminus_class.stubs(:new).returns(@terminus) - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) - @indirection.terminus_class = :foo - end - - it "should provide a method for setting the cache terminus class" do - @indirection.should respond_to(:cache_class=) - end - - it "should fail to cache if no cache type has been specified" do - proc { @indirection.cache }.should raise_error(Puppet::DevError) - end - - it "should fail to set the cache class when the cache class name is an empty string" do - proc { @indirection.cache_class = "" }.should raise_error(ArgumentError) - end - - it "should allow resetting the cache_class to nil" do - @indirection.cache_class = nil - @indirection.cache_class.should be_nil - end - - it "should fail to set the cache class when the specified cache class cannot be found" do - Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) - proc { @indirection.cache_class = :foo }.should raise_error(ArgumentError) - end - - after do - @indirection.delete - end - end - - describe "when using a cache" do - before :each do - Puppet.settings.stubs(:value).with("test_terminus").returns("test_terminus") - @terminus_class = mock 'terminus_class' - @terminus = mock 'terminus' - @terminus_class.stubs(:new).returns(@terminus) - @cache = mock 'cache' - @cache_class = mock 'cache_class' - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @indirection.terminus_class = :test_terminus - end - - describe "and managing the cache terminus" do - it "should not create a cache terminus at initialization" do - # This is weird, because all of the code is in the setup. If we got - # new() called on the cache class, we'd get an exception here. - end - - it "should reuse the cache terminus" do - @cache_class.expects(:new).returns(@cache) - Puppet.settings.stubs(:value).with("test_cache").returns("cache_terminus") - @indirection.cache_class = :cache_terminus - @indirection.cache.should equal(@cache) - @indirection.cache.should equal(@cache) - end - end - - describe "and saving" do - end - - describe "and finding" do - end - - after :each do - @indirection.delete - end - end -end diff --git a/spec/unit/indirector/key/ca_spec.rb b/spec/unit/indirector/key/ca_spec.rb new file mode 100755 index 000000000..964b0b0ed --- /dev/null +++ b/spec/unit/indirector/key/ca_spec.rb @@ -0,0 +1,28 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-7. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/key/ca' + +describe Puppet::SSL::Key::Ca do + it "should have documentation" do + Puppet::SSL::Key::Ca.doc.should be_instance_of(String) + end + + it "should use the :privatekeydir as the collection directory" do + Puppet.settings.expects(:value).with(:privatekeydir).returns "/key/dir" + Puppet::SSL::Key::Ca.collection_directory.should == "/key/dir" + end + + it "should store the ca key at the :cakey location" do + Puppet.settings.stubs(:use) + Puppet.settings.stubs(:value).returns "whatever" + Puppet.settings.stubs(:value).with(:cakey).returns "/ca/key" + file = Puppet::SSL::Key::Ca.new + file.stubs(:ca?).returns true + file.path("whatever").should == "/ca/key" + end +end diff --git a/spec/unit/indirector/key/ca_spec_spec.rb b/spec/unit/indirector/key/ca_spec_spec.rb deleted file mode 100755 index 964b0b0ed..000000000 --- a/spec/unit/indirector/key/ca_spec_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-7. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/key/ca' - -describe Puppet::SSL::Key::Ca do - it "should have documentation" do - Puppet::SSL::Key::Ca.doc.should be_instance_of(String) - end - - it "should use the :privatekeydir as the collection directory" do - Puppet.settings.expects(:value).with(:privatekeydir).returns "/key/dir" - Puppet::SSL::Key::Ca.collection_directory.should == "/key/dir" - end - - it "should store the ca key at the :cakey location" do - Puppet.settings.stubs(:use) - Puppet.settings.stubs(:value).returns "whatever" - Puppet.settings.stubs(:value).with(:cakey).returns "/ca/key" - file = Puppet::SSL::Key::Ca.new - file.stubs(:ca?).returns true - file.path("whatever").should == "/ca/key" - end -end diff --git a/spec/unit/indirector/key/file_spec.rb b/spec/unit/indirector/key/file_spec.rb new file mode 100755 index 000000000..f365bfd60 --- /dev/null +++ b/spec/unit/indirector/key/file_spec.rb @@ -0,0 +1,104 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-7. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/key/file' + +describe Puppet::SSL::Key::File do + it "should have documentation" do + Puppet::SSL::Key::File.doc.should be_instance_of(String) + end + + it "should use the :privatekeydir as the collection directory" do + Puppet.settings.expects(:value).with(:privatekeydir).returns "/key/dir" + Puppet::SSL::Key::File.collection_directory.should == "/key/dir" + end + + it "should store the ca key at the :cakey location" do + Puppet.settings.stubs(:use) + Puppet.settings.stubs(:value).returns "whatever" + Puppet.settings.stubs(:value).with(:cakey).returns "/ca/key" + file = Puppet::SSL::Key::File.new + file.stubs(:ca?).returns true + file.path("whatever").should == "/ca/key" + end + + describe "when choosing the path for the public key" do + it "should use the :capub setting location if the key is for the certificate authority" do + Puppet.settings.stubs(:value).returns "/fake/dir" + Puppet.settings.stubs(:value).with(:capub).returns "/ca/pubkey" + Puppet.settings.stubs(:use) + + @searcher = Puppet::SSL::Key::File.new + @searcher.stubs(:ca?).returns true + @searcher.public_key_path("whatever").should == "/ca/pubkey" + end + + it "should use the host name plus '.pem' in :publickeydir for normal hosts" do + Puppet.settings.stubs(:value).with(:privatekeydir).returns "/private/key/dir" + Puppet.settings.stubs(:value).with(:publickeydir).returns "/public/key/dir" + Puppet.settings.stubs(:use) + + @searcher = Puppet::SSL::Key::File.new + @searcher.stubs(:ca?).returns false + @searcher.public_key_path("whatever").should == "/public/key/dir/whatever.pem" + end + end + + describe "when managing private keys" do + before do + @searcher = Puppet::SSL::Key::File.new + + @private_key_path = File.join("/fake/key/path") + @public_key_path = File.join("/other/fake/key/path") + + @searcher.stubs(:public_key_path).returns @public_key_path + @searcher.stubs(:path).returns @private_key_path + + FileTest.stubs(:directory?).returns true + FileTest.stubs(:writable?).returns true + + @public_key = stub 'public_key' + @real_key = stub 'sslkey', :public_key => @public_key + + @key = stub 'key', :name => "myname", :content => @real_key + + @request = stub 'request', :key => "myname", :instance => @key + end + + it "should save the public key when saving the private key" do + Puppet.settings.stubs(:writesub) + + fh = mock 'filehandle' + + Puppet.settings.expects(:writesub).with(:publickeydir, @public_key_path).yields fh + @public_key.expects(:to_pem).returns "my pem" + + fh.expects(:print).with "my pem" + + @searcher.save(@request) + end + + it "should destroy the public key when destroying the private key" do + File.stubs(:unlink).with(@private_key_path) + FileTest.stubs(:exist?).with(@private_key_path).returns true + FileTest.expects(:exist?).with(@public_key_path).returns true + File.expects(:unlink).with(@public_key_path) + + @searcher.destroy(@request) + end + + it "should not fail if the public key does not exist when deleting the private key" do + File.stubs(:unlink).with(@private_key_path) + + FileTest.stubs(:exist?).with(@private_key_path).returns true + FileTest.expects(:exist?).with(@public_key_path).returns false + File.expects(:unlink).with(@public_key_path).never + + @searcher.destroy(@request) + end + end +end diff --git a/spec/unit/indirector/key/file_spec_spec.rb b/spec/unit/indirector/key/file_spec_spec.rb deleted file mode 100755 index f365bfd60..000000000 --- a/spec/unit/indirector/key/file_spec_spec.rb +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-7. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/key/file' - -describe Puppet::SSL::Key::File do - it "should have documentation" do - Puppet::SSL::Key::File.doc.should be_instance_of(String) - end - - it "should use the :privatekeydir as the collection directory" do - Puppet.settings.expects(:value).with(:privatekeydir).returns "/key/dir" - Puppet::SSL::Key::File.collection_directory.should == "/key/dir" - end - - it "should store the ca key at the :cakey location" do - Puppet.settings.stubs(:use) - Puppet.settings.stubs(:value).returns "whatever" - Puppet.settings.stubs(:value).with(:cakey).returns "/ca/key" - file = Puppet::SSL::Key::File.new - file.stubs(:ca?).returns true - file.path("whatever").should == "/ca/key" - end - - describe "when choosing the path for the public key" do - it "should use the :capub setting location if the key is for the certificate authority" do - Puppet.settings.stubs(:value).returns "/fake/dir" - Puppet.settings.stubs(:value).with(:capub).returns "/ca/pubkey" - Puppet.settings.stubs(:use) - - @searcher = Puppet::SSL::Key::File.new - @searcher.stubs(:ca?).returns true - @searcher.public_key_path("whatever").should == "/ca/pubkey" - end - - it "should use the host name plus '.pem' in :publickeydir for normal hosts" do - Puppet.settings.stubs(:value).with(:privatekeydir).returns "/private/key/dir" - Puppet.settings.stubs(:value).with(:publickeydir).returns "/public/key/dir" - Puppet.settings.stubs(:use) - - @searcher = Puppet::SSL::Key::File.new - @searcher.stubs(:ca?).returns false - @searcher.public_key_path("whatever").should == "/public/key/dir/whatever.pem" - end - end - - describe "when managing private keys" do - before do - @searcher = Puppet::SSL::Key::File.new - - @private_key_path = File.join("/fake/key/path") - @public_key_path = File.join("/other/fake/key/path") - - @searcher.stubs(:public_key_path).returns @public_key_path - @searcher.stubs(:path).returns @private_key_path - - FileTest.stubs(:directory?).returns true - FileTest.stubs(:writable?).returns true - - @public_key = stub 'public_key' - @real_key = stub 'sslkey', :public_key => @public_key - - @key = stub 'key', :name => "myname", :content => @real_key - - @request = stub 'request', :key => "myname", :instance => @key - end - - it "should save the public key when saving the private key" do - Puppet.settings.stubs(:writesub) - - fh = mock 'filehandle' - - Puppet.settings.expects(:writesub).with(:publickeydir, @public_key_path).yields fh - @public_key.expects(:to_pem).returns "my pem" - - fh.expects(:print).with "my pem" - - @searcher.save(@request) - end - - it "should destroy the public key when destroying the private key" do - File.stubs(:unlink).with(@private_key_path) - FileTest.stubs(:exist?).with(@private_key_path).returns true - FileTest.expects(:exist?).with(@public_key_path).returns true - File.expects(:unlink).with(@public_key_path) - - @searcher.destroy(@request) - end - - it "should not fail if the public key does not exist when deleting the private key" do - File.stubs(:unlink).with(@private_key_path) - - FileTest.stubs(:exist?).with(@private_key_path).returns true - FileTest.expects(:exist?).with(@public_key_path).returns false - File.expects(:unlink).with(@public_key_path).never - - @searcher.destroy(@request) - end - end -end diff --git a/spec/unit/indirector/ldap_spec.rb b/spec/unit/indirector/ldap_spec.rb new file mode 100755 index 000000000..44df2b5bb --- /dev/null +++ b/spec/unit/indirector/ldap_spec.rb @@ -0,0 +1,143 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/indirector/ldap' + +describe Puppet::Indirector::Ldap do + before do + @indirection = stub 'indirection', :name => :testing + Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) + @ldap_class = Class.new(Puppet::Indirector::Ldap) do + def self.to_s + "Testing::Mytype" + end + end + + @connection = mock 'ldap' + + @searcher = @ldap_class.new + end + + describe "when searching ldap" do + before do + # Stub everything, and we can selectively replace with an expect as + # we need to for testing. + @searcher.stubs(:connection).returns(@connection) + @searcher.stubs(:search_filter).returns(:filter) + @searcher.stubs(:search_base).returns(:base) + @searcher.stubs(:process) + + @request = stub 'request', :key => "yay" + end + + it "should call the ldapsearch method with the search filter" do + @searcher.expects(:search_filter).with("yay").returns("yay's filter") + @searcher.expects(:ldapsearch).with("yay's filter") + @searcher.find @request + end + + it "should fail if no block is passed to the ldapsearch method" do + proc { @searcher.ldapsearch("blah") }.should raise_error(ArgumentError) + end + + it "should use the results of the ldapbase method as the ldap search base" do + @searcher.stubs(:search_base).returns("mybase") + @connection.expects(:search).with do |*args| + args[0].should == "mybase" + true + end + @searcher.find @request + end + + it "should default to the value of the :search_base setting as the result of the ldapbase method" do + Puppet.expects(:[]).with(:ldapbase).returns("myldapbase") + searcher = @ldap_class.new + searcher.search_base.should == "myldapbase" + end + + it "should use the results of the :search_attributes method as the list of attributes to return" do + @searcher.stubs(:search_attributes).returns(:myattrs) + @connection.expects(:search).with do |*args| + args[3].should == :myattrs + true + end + @searcher.find @request + end + + it "should use depth 2 when searching" do + @connection.expects(:search).with do |*args| + args[1].should == 2 + true + end + @searcher.find @request + end + + it "should call process() on the first found entry" do + @connection.expects(:search).yields("myresult") + @searcher.expects(:process).with("myresult") + @searcher.find @request + end + + it "should reconnect and retry the search if there is a failure" do + run = false + @connection.stubs(:search).with do |*args| + if run + true + else + run = true + raise "failed" + end + end.yields("myresult") + @searcher.expects(:process).with("myresult") + + @searcher.find @request + end + + it "should not reconnect on failure more than once" do + count = 0 + @connection.stubs(:search).with do |*args| + count += 1 + raise ArgumentError, "yay" + end + proc { @searcher.find(@request) }.should raise_error(Puppet::Error) + count.should == 2 + end + + it "should return true if an entry is found" do + @connection.expects(:search).yields("result") + @searcher.ldapsearch("whatever") { |r| }.should be_true + end + end + + describe "when connecting to ldap" do + confine "LDAP is not available" => Puppet.features.ldap? + + it "should create and start a Util::Ldap::Connection instance" do + conn = mock 'connection', :connection => "myconn", :start => nil + Puppet::Util::Ldap::Connection.expects(:instance).returns conn + + @searcher.connection.should == "myconn" + end + + it "should only create the ldap connection when asked for it the first time" do + conn = mock 'connection', :connection => "myconn", :start => nil + Puppet::Util::Ldap::Connection.expects(:instance).returns conn + + @searcher.connection + end + + it "should cache the connection" do + conn = mock 'connection', :connection => "myconn", :start => nil + Puppet::Util::Ldap::Connection.expects(:instance).returns conn + + @searcher.connection.should equal(@searcher.connection) + end + end + + describe "when reconnecting to ldap" do + confine "Not running on culain as root" => (Puppet.features.root? and Facter.value("hostname") == "culain") + + it "should reconnect to ldap when connections are lost" + end +end diff --git a/spec/unit/indirector/ldap_spec_spec.rb b/spec/unit/indirector/ldap_spec_spec.rb deleted file mode 100755 index 44df2b5bb..000000000 --- a/spec/unit/indirector/ldap_spec_spec.rb +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/indirector/ldap' - -describe Puppet::Indirector::Ldap do - before do - @indirection = stub 'indirection', :name => :testing - Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - @ldap_class = Class.new(Puppet::Indirector::Ldap) do - def self.to_s - "Testing::Mytype" - end - end - - @connection = mock 'ldap' - - @searcher = @ldap_class.new - end - - describe "when searching ldap" do - before do - # Stub everything, and we can selectively replace with an expect as - # we need to for testing. - @searcher.stubs(:connection).returns(@connection) - @searcher.stubs(:search_filter).returns(:filter) - @searcher.stubs(:search_base).returns(:base) - @searcher.stubs(:process) - - @request = stub 'request', :key => "yay" - end - - it "should call the ldapsearch method with the search filter" do - @searcher.expects(:search_filter).with("yay").returns("yay's filter") - @searcher.expects(:ldapsearch).with("yay's filter") - @searcher.find @request - end - - it "should fail if no block is passed to the ldapsearch method" do - proc { @searcher.ldapsearch("blah") }.should raise_error(ArgumentError) - end - - it "should use the results of the ldapbase method as the ldap search base" do - @searcher.stubs(:search_base).returns("mybase") - @connection.expects(:search).with do |*args| - args[0].should == "mybase" - true - end - @searcher.find @request - end - - it "should default to the value of the :search_base setting as the result of the ldapbase method" do - Puppet.expects(:[]).with(:ldapbase).returns("myldapbase") - searcher = @ldap_class.new - searcher.search_base.should == "myldapbase" - end - - it "should use the results of the :search_attributes method as the list of attributes to return" do - @searcher.stubs(:search_attributes).returns(:myattrs) - @connection.expects(:search).with do |*args| - args[3].should == :myattrs - true - end - @searcher.find @request - end - - it "should use depth 2 when searching" do - @connection.expects(:search).with do |*args| - args[1].should == 2 - true - end - @searcher.find @request - end - - it "should call process() on the first found entry" do - @connection.expects(:search).yields("myresult") - @searcher.expects(:process).with("myresult") - @searcher.find @request - end - - it "should reconnect and retry the search if there is a failure" do - run = false - @connection.stubs(:search).with do |*args| - if run - true - else - run = true - raise "failed" - end - end.yields("myresult") - @searcher.expects(:process).with("myresult") - - @searcher.find @request - end - - it "should not reconnect on failure more than once" do - count = 0 - @connection.stubs(:search).with do |*args| - count += 1 - raise ArgumentError, "yay" - end - proc { @searcher.find(@request) }.should raise_error(Puppet::Error) - count.should == 2 - end - - it "should return true if an entry is found" do - @connection.expects(:search).yields("result") - @searcher.ldapsearch("whatever") { |r| }.should be_true - end - end - - describe "when connecting to ldap" do - confine "LDAP is not available" => Puppet.features.ldap? - - it "should create and start a Util::Ldap::Connection instance" do - conn = mock 'connection', :connection => "myconn", :start => nil - Puppet::Util::Ldap::Connection.expects(:instance).returns conn - - @searcher.connection.should == "myconn" - end - - it "should only create the ldap connection when asked for it the first time" do - conn = mock 'connection', :connection => "myconn", :start => nil - Puppet::Util::Ldap::Connection.expects(:instance).returns conn - - @searcher.connection - end - - it "should cache the connection" do - conn = mock 'connection', :connection => "myconn", :start => nil - Puppet::Util::Ldap::Connection.expects(:instance).returns conn - - @searcher.connection.should equal(@searcher.connection) - end - end - - describe "when reconnecting to ldap" do - confine "Not running on culain as root" => (Puppet.features.root? and Facter.value("hostname") == "culain") - - it "should reconnect to ldap when connections are lost" - end -end diff --git a/spec/unit/indirector/memory_spec.rb b/spec/unit/indirector/memory_spec.rb new file mode 100755 index 000000000..3b754a1eb --- /dev/null +++ b/spec/unit/indirector/memory_spec.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/indirector/memory' + +require 'shared_behaviours/memory_terminus' + +describe Puppet::Indirector::Memory do + it_should_behave_like "A Memory Terminus" + + before do + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @model = mock 'model' + @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model + Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) + + @memory_class = Class.new(Puppet::Indirector::Memory) do + def self.to_s + "Mystuff::Testing" + end + end + + @searcher = @memory_class.new + @name = "me" + @instance = stub 'instance', :name => @name + + @request = stub 'request', :key => @name, :instance => @instance + end +end diff --git a/spec/unit/indirector/memory_spec_spec.rb b/spec/unit/indirector/memory_spec_spec.rb deleted file mode 100755 index 3b754a1eb..000000000 --- a/spec/unit/indirector/memory_spec_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector/memory' - -require 'shared_behaviours/memory_terminus' - -describe Puppet::Indirector::Memory do - it_should_behave_like "A Memory Terminus" - - before do - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @model = mock 'model' - @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model - Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - - @memory_class = Class.new(Puppet::Indirector::Memory) do - def self.to_s - "Mystuff::Testing" - end - end - - @searcher = @memory_class.new - @name = "me" - @instance = stub 'instance', :name => @name - - @request = stub 'request', :key => @name, :instance => @instance - end -end diff --git a/spec/unit/indirector/node/active_record_spec.rb b/spec/unit/indirector/node/active_record_spec.rb new file mode 100755 index 000000000..1d90295cf --- /dev/null +++ b/spec/unit/indirector/node/active_record_spec.rb @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/node' + +describe "Puppet::Node::ActiveRecord" do + confine "Missing Rails" => Puppet.features.rails? + before do + require 'puppet/indirector/node/active_record' + end + + it "should be a subclass of the ActiveRecord terminus class" do + Puppet::Node::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) + end + + it "should use Puppet::Rails::Host as its ActiveRecord model" do + Puppet::Node::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) + end + + it "should call fact_merge when a node is found" do + db_instance = stub 'db_instance' + Puppet::Node::ActiveRecord.ar_model.expects(:find_by_name).returns db_instance + + node = Puppet::Node.new("foo") + db_instance.expects(:to_puppet).returns node + ar = Puppet::Node::ActiveRecord.new + + node.expects(:fact_merge) + + request = Puppet::Indirector::Request.new(:node, :find, "what.ever") + ar.find(request) + end +end diff --git a/spec/unit/indirector/node/active_record_spec_spec.rb b/spec/unit/indirector/node/active_record_spec_spec.rb deleted file mode 100755 index 1d90295cf..000000000 --- a/spec/unit/indirector/node/active_record_spec_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/node' - -describe "Puppet::Node::ActiveRecord" do - confine "Missing Rails" => Puppet.features.rails? - before do - require 'puppet/indirector/node/active_record' - end - - it "should be a subclass of the ActiveRecord terminus class" do - Puppet::Node::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) - end - - it "should use Puppet::Rails::Host as its ActiveRecord model" do - Puppet::Node::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) - end - - it "should call fact_merge when a node is found" do - db_instance = stub 'db_instance' - Puppet::Node::ActiveRecord.ar_model.expects(:find_by_name).returns db_instance - - node = Puppet::Node.new("foo") - db_instance.expects(:to_puppet).returns node - ar = Puppet::Node::ActiveRecord.new - - node.expects(:fact_merge) - - request = Puppet::Indirector::Request.new(:node, :find, "what.ever") - ar.find(request) - end -end diff --git a/spec/unit/indirector/node/exec_spec.rb b/spec/unit/indirector/node/exec_spec.rb new file mode 100755 index 000000000..09f13ab90 --- /dev/null +++ b/spec/unit/indirector/node/exec_spec.rb @@ -0,0 +1,69 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/node/exec' + +describe Puppet::Node::Exec do + before do + @indirection = mock 'indirection' + Puppet.settings.stubs(:value).with(:external_nodes).returns("/echo") + @searcher = Puppet::Node::Exec.new + end + + describe "when constructing the command to run" do + it "should use the external_node script as the command" do + Puppet.expects(:[]).with(:external_nodes).returns("/bin/echo") + @searcher.command.should == %w{/bin/echo} + end + + it "should throw an exception if no external node command is set" do + Puppet.expects(:[]).with(:external_nodes).returns("none") + proc { @searcher.find(stub('request', :key => "foo")) }.should raise_error(ArgumentError) + end + end + + describe "when handling the results of the command" do + before do + @node = stub 'node', :fact_merge => nil + @name = "yay" + Puppet::Node.expects(:new).with(@name).returns(@node) + @result = {} + # Use a local variable so the reference is usable in the execute() definition. + result = @result + @searcher.meta_def(:execute) do |command| + return YAML.dump(result) + end + + @request = stub 'request', :key => @name + end + + it "should translate the YAML into a Node instance" do + # Use an empty hash + @searcher.find(@request).should equal(@node) + end + + it "should set the resulting parameters as the node parameters" do + @result[:parameters] = {"a" => "b", "c" => "d"} + @node.expects(:parameters=).with "a" => "b", "c" => "d" + @searcher.find(@request) + end + + it "should set the resulting classes as the node classes" do + @result[:classes] = %w{one two} + @node.expects(:classes=).with %w{one two} + @searcher.find(@request) + end + + it "should merge the node's facts with its parameters" do + @node.expects(:fact_merge) + @searcher.find(@request) + end + + it "should set the node's environment if one is provided" do + @result[:environment] = "yay" + @node.expects(:environment=).with "yay" + @searcher.find(@request) + end + end +end diff --git a/spec/unit/indirector/node/exec_spec_spec.rb b/spec/unit/indirector/node/exec_spec_spec.rb deleted file mode 100755 index 09f13ab90..000000000 --- a/spec/unit/indirector/node/exec_spec_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/node/exec' - -describe Puppet::Node::Exec do - before do - @indirection = mock 'indirection' - Puppet.settings.stubs(:value).with(:external_nodes).returns("/echo") - @searcher = Puppet::Node::Exec.new - end - - describe "when constructing the command to run" do - it "should use the external_node script as the command" do - Puppet.expects(:[]).with(:external_nodes).returns("/bin/echo") - @searcher.command.should == %w{/bin/echo} - end - - it "should throw an exception if no external node command is set" do - Puppet.expects(:[]).with(:external_nodes).returns("none") - proc { @searcher.find(stub('request', :key => "foo")) }.should raise_error(ArgumentError) - end - end - - describe "when handling the results of the command" do - before do - @node = stub 'node', :fact_merge => nil - @name = "yay" - Puppet::Node.expects(:new).with(@name).returns(@node) - @result = {} - # Use a local variable so the reference is usable in the execute() definition. - result = @result - @searcher.meta_def(:execute) do |command| - return YAML.dump(result) - end - - @request = stub 'request', :key => @name - end - - it "should translate the YAML into a Node instance" do - # Use an empty hash - @searcher.find(@request).should equal(@node) - end - - it "should set the resulting parameters as the node parameters" do - @result[:parameters] = {"a" => "b", "c" => "d"} - @node.expects(:parameters=).with "a" => "b", "c" => "d" - @searcher.find(@request) - end - - it "should set the resulting classes as the node classes" do - @result[:classes] = %w{one two} - @node.expects(:classes=).with %w{one two} - @searcher.find(@request) - end - - it "should merge the node's facts with its parameters" do - @node.expects(:fact_merge) - @searcher.find(@request) - end - - it "should set the node's environment if one is provided" do - @result[:environment] = "yay" - @node.expects(:environment=).with "yay" - @searcher.find(@request) - end - end -end diff --git a/spec/unit/indirector/node/ldap_spec.rb b/spec/unit/indirector/node/ldap_spec.rb new file mode 100755 index 000000000..6f53b6ccf --- /dev/null +++ b/spec/unit/indirector/node/ldap_spec.rb @@ -0,0 +1,456 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/node/ldap' + +describe Puppet::Node::Ldap do + before do + Puppet::Node::Facts.stubs(:terminus_class).returns :yaml + end + + describe "when searching for a single node" do + before :each do + @searcher = Puppet::Node::Ldap.new + + @name = "mynode.domain.com" + @node = stub 'node', :name => @name, :name= => nil + @node.stub_everything + + Puppet::Node.stubs(:new).returns(@node) + + @request = stub 'request', :key => @name + end + + it "should convert the hostname into a search filter" do + entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {"environment" => 'production'} + @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{@name}))").yields entry + @searcher.name2hash(@name, 'production', 'parent') + end + + it "should convert any found entry into a hash" do + entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {"environment" => 'production'} + @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{@name}))").yields entry + myhash = {"myhash" => true, :environment => 'production'} + @searcher.expects(:entry2hash).with(entry).returns myhash + @searcher.name2hash(@name, 'production', 'parent').should == myhash + end + + # This heavily tests our entry2hash method, so we don't have to stub out the stupid entry information any more. + describe "when an ldap entry is found" do + before do + @entry = stub 'entry', :dn => 'cn=mynode,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} + @searcher.stubs(:ldapsearch).yields @entry + end + + it "should convert the entry to a hash" do + @searcher.entry2hash(@entry).should be_instance_of(Hash) + end + + it "should add the entry's common name to the hash if fqdn if false" do + @searcher.entry2hash(@entry,fqdn = false)[:name].should == "mynode" + end + + it "should add the entry's fqdn name to the hash if fqdn if true" do + @searcher.entry2hash(@entry,fqdn = true)[:name].should == "mynode.madstop.com" + end + + it "should add all of the entry's classes to the hash" do + @entry.stubs(:vals).with("puppetclass").returns %w{one two} + @searcher.entry2hash(@entry)[:classes].should == %w{one two} + end + + it "should deduplicate class values" do + @entry.stubs(:to_hash).returns({}) + @searcher.stubs(:class_attributes).returns(%w{one two}) + @entry.stubs(:vals).with("one").returns(%w{a b}) + @entry.stubs(:vals).with("two").returns(%w{b c}) + @searcher.entry2hash(@entry)[:classes].should == %w{a b c} + end + + it "should add the entry's environment to the hash" do + @entry.stubs(:to_hash).returns("environment" => %w{production}) + @searcher.entry2hash(@entry)[:environment].should == "production" + end + + it "should add all stacked parameters as parameters in the hash" do + @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) + result = @searcher.entry2hash(@entry) + result[:parameters]["one"].should == "two" + result[:parameters]["three"].should == "four" + end + + it "should not add the stacked parameter as a normal parameter" do + @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) + @entry.stubs(:to_hash).returns("puppetvar" => %w{one=two three=four}) + @searcher.entry2hash(@entry)[:parameters]["puppetvar"].should be_nil + end + + it "should add all other attributes as parameters in the hash" do + @entry.stubs(:to_hash).returns("foo" => %w{one two}) + @searcher.entry2hash(@entry)[:parameters]["foo"].should == %w{one two} + end + + it "should return single-value parameters as strings, not arrays" do + @entry.stubs(:to_hash).returns("foo" => %w{one}) + @searcher.entry2hash(@entry)[:parameters]["foo"].should == "one" + end + + it "should convert 'true' values to the boolean 'true'" do + @entry.stubs(:to_hash).returns({"one" => ["true"]}) + @searcher.entry2hash(@entry)[:parameters]["one"].should == true + end + + it "should convert 'false' values to the boolean 'false'" do + @entry.stubs(:to_hash).returns({"one" => ["false"]}) + @searcher.entry2hash(@entry)[:parameters]["one"].should == false + end + + it "should convert 'true' values to the boolean 'true' inside an array" do + @entry.stubs(:to_hash).returns({"one" => ["true", "other"]}) + @searcher.entry2hash(@entry)[:parameters]["one"].should == [true, "other"] + end + + it "should convert 'false' values to the boolean 'false' inside an array" do + @entry.stubs(:to_hash).returns({"one" => ["false", "other"]}) + @searcher.entry2hash(@entry)[:parameters]["one"].should == [false, "other"] + end + + it "should add the parent's name if present" do + @entry.stubs(:vals).with("parentnode").returns(%w{foo}) + @searcher.entry2hash(@entry)[:parent].should == "foo" + end + + it "should fail if more than one parent is specified" do + @entry.stubs(:vals).with("parentnode").returns(%w{foo}) + @searcher.entry2hash(@entry)[:parent].should == "foo" + end + end + + it "should search first for the provided key" do + @searcher.expects(:name2hash).with("mynode.domain.com", 'production', 'child').returns({}) + @searcher.find(@request) + end + + it "should search for the short version of the provided key if the key looks like a hostname and no results are found for the key itself" do + @searcher.expects(:name2hash).with("mynode.domain.com", 'production', 'child').returns(nil) + @searcher.expects(:name2hash).with("mynode", 'production', 'child').returns({}) + @searcher.find(@request) + end + + it "should search for default information if no information can be found for the key" do + @searcher.expects(:name2hash).with("mynode.domain.com", 'production', 'child').returns(nil) + @searcher.expects(:name2hash).with("mynode", 'production', 'child').returns(nil) + @searcher.expects(:name2hash).with("default", 'production', 'child').returns({}) + @searcher.find(@request) + end + + it "should return nil if no results are found in ldap" do + @searcher.stubs(:name2hash).returns nil + @searcher.find(@request).should be_nil + end + + it "should return a node object if results are found in ldap" do + @searcher.stubs(:name2hash).returns({}) + @searcher.find(@request).should equal(@node) + end + + describe "and node information is found in LDAP" do + before do + @result = {} + @searcher.stubs(:name2hash).returns @result + end + + it "should create the node with the correct name, even if it was found by a different name" do + @searcher.expects(:name2hash).with("mynode.domain.com", 'production', 'child').returns nil + @searcher.expects(:name2hash).with("mynode", 'production', 'child').returns @result + + Puppet::Node.expects(:new).with("mynode.domain.com").returns @node + @searcher.find(@request) + end + + it "should add any classes from ldap" do + @result[:classes] = %w[a b c d] + @node.expects(:classes=).with(%w{a b c d}) + @searcher.find(@request) + end + + it "should add all entry attributes as node parameters" do + @result[:parameters] = {"one" => "two", "three" => "four"} + @node.expects(:parameters=).with("one" => "two", "three" => "four") + @searcher.find(@request) + end + + it "should set the node's environment to the environment of the results" do + @result[:environment] = "test" + @node.expects(:environment=).with("test") + @searcher.find(@request) + end + + it "should retain false parameter values" do + @result[:parameters] = {} + @result[:parameters]["one"] = false + @node.expects(:parameters=).with("one" => false) + @searcher.find(@request) + end + + it "should merge the node's facts after the parameters from ldap are assigned" do + # Make sure we've got data to start with, so the parameters are actually set. + @result[:parameters] = {} + @result[:parameters]["one"] = "yay" + + # A hackish way to enforce order. + set = false + @node.expects(:parameters=).with { |*args| set = true } + @node.expects(:fact_merge).with { |*args| raise "Facts were merged before parameters were set" unless set; true } + + @searcher.find(@request) + end + + describe "and a parent node is specified" do + before do + @entry = {:classes => [], :parameters => {}} + @parent = {:classes => [], :parameters => {}} + @parent_parent = {:classes => [], :parameters => {}} + + @searcher.stubs(:name2hash).with{|name, env, mode| name == @name}.returns(@entry) + @searcher.stubs(:name2hash).with{|name, env, mode| name == 'parent'}.returns(@parent) + @searcher.stubs(:name2hash).with{|name, env, mode| name == 'parent_parent'}.returns(@parent_parent) + + @searcher.stubs(:parent_attribute).returns(:parent) + end + + it "should search for the parent node" do + @entry[:parent] = "parent" + + @searcher.find(@request) + end + + it "should fail if the parent cannot be found" do + @entry[:parent] = "parent" + + @searcher.expects(:name2hash).with('parent', 'production', 'parent').returns nil + + proc { @searcher.find(@request) }.should raise_error(Puppet::Error) + end + + it "should add any parent classes to the node's classes" do + @entry[:parent] = "parent" + @entry[:classes] = %w{a b} + + @parent[:classes] = %w{c d} + + @node.expects(:classes=).with(%w{a b c d}) + @searcher.find(@request) + end + + it "should add any parent parameters to the node's parameters" do + @entry[:parent] = "parent" + @entry[:parameters]["one"] = "two" + + @parent[:parameters]["three"] = "four" + + @node.expects(:parameters=).with("one" => "two", "three" => "four") + @searcher.find(@request) + end + + it "should prefer node parameters over parent parameters" do + @entry[:parent] = "parent" + @entry[:parameters]["one"] = "two" + + @parent[:parameters]["one"] = "three" + + @node.expects(:parameters=).with("one" => "two") + @searcher.find(@request) + end + + it "should use the parent's environment if the node has none" do + @entry[:parent] = "parent" + + @parent[:environment] = "parent" + + @node.stubs(:parameters=) + @node.expects(:environment=).with("parent") + @searcher.find(@request) + end + + it "should prefer the node's environment to the parent's" do + @entry[:parent] = "parent" + @entry[:environment] = "child" + + @parent[:environment] = "parent" + + @node.stubs(:parameters=) + @node.expects(:environment=).with("child") + @searcher.find(@request) + end + + it "should recursively look up parent information" do + @entry[:parent] = "parent" + @entry[:parameters]["one"] = "two" + + @parent[:parent] = "parent_parent" + @parent[:parameters]["three"] = "four" + + @parent_parent[:parameters]["five"] = "six" + + @node.expects(:parameters=).with("one" => "two", "three" => "four", "five" => "six") + @searcher.find(@request) + end + + it "should not allow loops in parent declarations" do + @entry[:parent] = "parent" + @parent[:parent] = @name + proc { @searcher.find(@request) }.should raise_error(ArgumentError) + end + end + end + end + + describe "when searching for multiple nodes" do + before :each do + @searcher = Puppet::Node::Ldap.new + @options = {} + @request = stub 'request', :key => "foo", :options => @options + + Puppet::Node::Facts.stubs(:terminus_class).returns :yaml + end + + it "should find all nodes if no arguments are provided" do + @searcher.expects(:ldapsearch).with("(objectclass=puppetClient)") + # LAK:NOTE The search method requires an essentially bogus key. It's + # an API problem that I don't really know how to fix. + @searcher.search @request + end + + describe "and a class is specified" do + it "should find all nodes that are members of that class" do + @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one))") + + @options[:class] = "one" + @searcher.search @request + end + end + + describe "multiple classes are specified" do + it "should find all nodes that are members of all classes" do + @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one)(puppetclass=two))") + @options[:class] = %w{one two} + @searcher.search @request + end + end + + it "should process each found entry" do + # .yields can't be used to yield multiple values :/ + @searcher.expects(:ldapsearch).yields("one") + @searcher.expects(:entry2hash).with("one",nil).returns(:name => "foo") + @searcher.search @request + end + + it "should return a node for each processed entry with the name from the entry" do + @searcher.expects(:ldapsearch).yields("whatever") + @searcher.expects(:entry2hash).with("whatever",nil).returns(:name => "foo") + result = @searcher.search(@request) + result[0].should be_instance_of(Puppet::Node) + result[0].name.should == "foo" + end + + it "should merge each node's facts" do + node = mock 'node' + Puppet::Node.expects(:new).with("foo").returns node + node.expects(:fact_merge) + @searcher.stubs(:ldapsearch).yields("one") + @searcher.stubs(:entry2hash).with("one",nil).returns(:name => "foo") + @searcher.search(@request) + end + + it "should pass the request's fqdn option to entry2hash" do + node = mock 'node' + @options[:fqdn] = :hello + Puppet::Node.stubs(:new).with("foo").returns node + node.stubs(:fact_merge) + @searcher.stubs(:ldapsearch).yields("one") + @searcher.expects(:entry2hash).with("one",:hello).returns(:name => "foo") + @searcher.search(@request) + end + end +end + +describe Puppet::Node::Ldap, " when developing the search query" do + before do + @searcher = Puppet::Node::Ldap.new + end + + it "should return the value of the :ldapclassattrs split on commas as the class attributes" do + Puppet.stubs(:[]).with(:ldapclassattrs).returns("one,two") + @searcher.class_attributes.should == %w{one two} + end + + it "should return nil as the parent attribute if the :ldapparentattr is set to an empty string" do + Puppet.stubs(:[]).with(:ldapparentattr).returns("") + @searcher.parent_attribute.should be_nil + end + + it "should return the value of the :ldapparentattr as the parent attribute" do + Puppet.stubs(:[]).with(:ldapparentattr).returns("pere") + @searcher.parent_attribute.should == "pere" + end + + it "should use the value of the :ldapstring as the search filter" do + Puppet.stubs(:[]).with(:ldapstring).returns("mystring") + @searcher.search_filter("testing").should == "mystring" + end + + it "should replace '%s' with the node name in the search filter if it is present" do + Puppet.stubs(:[]).with(:ldapstring).returns("my%sstring") + @searcher.search_filter("testing").should == "mytestingstring" + end + + it "should not modify the global :ldapstring when replacing '%s' in the search filter" do + filter = mock 'filter' + filter.expects(:include?).with("%s").returns(true) + filter.expects(:gsub).with("%s", "testing").returns("mynewstring") + Puppet.stubs(:[]).with(:ldapstring).returns(filter) + @searcher.search_filter("testing").should == "mynewstring" + end +end + +describe Puppet::Node::Ldap, " when deciding attributes to search for" do + before do + @searcher = Puppet::Node::Ldap.new + end + + it "should use 'nil' if the :ldapattrs setting is 'all'" do + Puppet.stubs(:[]).with(:ldapattrs).returns("all") + @searcher.search_attributes.should be_nil + end + + it "should split the value of :ldapattrs on commas and use the result as the attribute list" do + Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") + @searcher.stubs(:class_attributes).returns([]) + @searcher.stubs(:parent_attribute).returns(nil) + @searcher.search_attributes.should == %w{one two} + end + + it "should add the class attributes to the search attributes if not returning all attributes" do + Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") + @searcher.stubs(:class_attributes).returns(%w{three four}) + @searcher.stubs(:parent_attribute).returns(nil) + # Sort them so i don't have to care about return order + @searcher.search_attributes.sort.should == %w{one two three four}.sort + end + + it "should add the parent attribute to the search attributes if not returning all attributes" do + Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") + @searcher.stubs(:class_attributes).returns([]) + @searcher.stubs(:parent_attribute).returns("parent") + @searcher.search_attributes.sort.should == %w{one two parent}.sort + end + + it "should not add nil parent attributes to the search attributes" do + Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") + @searcher.stubs(:class_attributes).returns([]) + @searcher.stubs(:parent_attribute).returns(nil) + @searcher.search_attributes.should == %w{one two} + end +end diff --git a/spec/unit/indirector/node/ldap_spec_spec.rb b/spec/unit/indirector/node/ldap_spec_spec.rb deleted file mode 100755 index 6f53b6ccf..000000000 --- a/spec/unit/indirector/node/ldap_spec_spec.rb +++ /dev/null @@ -1,456 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/node/ldap' - -describe Puppet::Node::Ldap do - before do - Puppet::Node::Facts.stubs(:terminus_class).returns :yaml - end - - describe "when searching for a single node" do - before :each do - @searcher = Puppet::Node::Ldap.new - - @name = "mynode.domain.com" - @node = stub 'node', :name => @name, :name= => nil - @node.stub_everything - - Puppet::Node.stubs(:new).returns(@node) - - @request = stub 'request', :key => @name - end - - it "should convert the hostname into a search filter" do - entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {"environment" => 'production'} - @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{@name}))").yields entry - @searcher.name2hash(@name, 'production', 'parent') - end - - it "should convert any found entry into a hash" do - entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {"environment" => 'production'} - @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{@name}))").yields entry - myhash = {"myhash" => true, :environment => 'production'} - @searcher.expects(:entry2hash).with(entry).returns myhash - @searcher.name2hash(@name, 'production', 'parent').should == myhash - end - - # This heavily tests our entry2hash method, so we don't have to stub out the stupid entry information any more. - describe "when an ldap entry is found" do - before do - @entry = stub 'entry', :dn => 'cn=mynode,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} - @searcher.stubs(:ldapsearch).yields @entry - end - - it "should convert the entry to a hash" do - @searcher.entry2hash(@entry).should be_instance_of(Hash) - end - - it "should add the entry's common name to the hash if fqdn if false" do - @searcher.entry2hash(@entry,fqdn = false)[:name].should == "mynode" - end - - it "should add the entry's fqdn name to the hash if fqdn if true" do - @searcher.entry2hash(@entry,fqdn = true)[:name].should == "mynode.madstop.com" - end - - it "should add all of the entry's classes to the hash" do - @entry.stubs(:vals).with("puppetclass").returns %w{one two} - @searcher.entry2hash(@entry)[:classes].should == %w{one two} - end - - it "should deduplicate class values" do - @entry.stubs(:to_hash).returns({}) - @searcher.stubs(:class_attributes).returns(%w{one two}) - @entry.stubs(:vals).with("one").returns(%w{a b}) - @entry.stubs(:vals).with("two").returns(%w{b c}) - @searcher.entry2hash(@entry)[:classes].should == %w{a b c} - end - - it "should add the entry's environment to the hash" do - @entry.stubs(:to_hash).returns("environment" => %w{production}) - @searcher.entry2hash(@entry)[:environment].should == "production" - end - - it "should add all stacked parameters as parameters in the hash" do - @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) - result = @searcher.entry2hash(@entry) - result[:parameters]["one"].should == "two" - result[:parameters]["three"].should == "four" - end - - it "should not add the stacked parameter as a normal parameter" do - @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) - @entry.stubs(:to_hash).returns("puppetvar" => %w{one=two three=four}) - @searcher.entry2hash(@entry)[:parameters]["puppetvar"].should be_nil - end - - it "should add all other attributes as parameters in the hash" do - @entry.stubs(:to_hash).returns("foo" => %w{one two}) - @searcher.entry2hash(@entry)[:parameters]["foo"].should == %w{one two} - end - - it "should return single-value parameters as strings, not arrays" do - @entry.stubs(:to_hash).returns("foo" => %w{one}) - @searcher.entry2hash(@entry)[:parameters]["foo"].should == "one" - end - - it "should convert 'true' values to the boolean 'true'" do - @entry.stubs(:to_hash).returns({"one" => ["true"]}) - @searcher.entry2hash(@entry)[:parameters]["one"].should == true - end - - it "should convert 'false' values to the boolean 'false'" do - @entry.stubs(:to_hash).returns({"one" => ["false"]}) - @searcher.entry2hash(@entry)[:parameters]["one"].should == false - end - - it "should convert 'true' values to the boolean 'true' inside an array" do - @entry.stubs(:to_hash).returns({"one" => ["true", "other"]}) - @searcher.entry2hash(@entry)[:parameters]["one"].should == [true, "other"] - end - - it "should convert 'false' values to the boolean 'false' inside an array" do - @entry.stubs(:to_hash).returns({"one" => ["false", "other"]}) - @searcher.entry2hash(@entry)[:parameters]["one"].should == [false, "other"] - end - - it "should add the parent's name if present" do - @entry.stubs(:vals).with("parentnode").returns(%w{foo}) - @searcher.entry2hash(@entry)[:parent].should == "foo" - end - - it "should fail if more than one parent is specified" do - @entry.stubs(:vals).with("parentnode").returns(%w{foo}) - @searcher.entry2hash(@entry)[:parent].should == "foo" - end - end - - it "should search first for the provided key" do - @searcher.expects(:name2hash).with("mynode.domain.com", 'production', 'child').returns({}) - @searcher.find(@request) - end - - it "should search for the short version of the provided key if the key looks like a hostname and no results are found for the key itself" do - @searcher.expects(:name2hash).with("mynode.domain.com", 'production', 'child').returns(nil) - @searcher.expects(:name2hash).with("mynode", 'production', 'child').returns({}) - @searcher.find(@request) - end - - it "should search for default information if no information can be found for the key" do - @searcher.expects(:name2hash).with("mynode.domain.com", 'production', 'child').returns(nil) - @searcher.expects(:name2hash).with("mynode", 'production', 'child').returns(nil) - @searcher.expects(:name2hash).with("default", 'production', 'child').returns({}) - @searcher.find(@request) - end - - it "should return nil if no results are found in ldap" do - @searcher.stubs(:name2hash).returns nil - @searcher.find(@request).should be_nil - end - - it "should return a node object if results are found in ldap" do - @searcher.stubs(:name2hash).returns({}) - @searcher.find(@request).should equal(@node) - end - - describe "and node information is found in LDAP" do - before do - @result = {} - @searcher.stubs(:name2hash).returns @result - end - - it "should create the node with the correct name, even if it was found by a different name" do - @searcher.expects(:name2hash).with("mynode.domain.com", 'production', 'child').returns nil - @searcher.expects(:name2hash).with("mynode", 'production', 'child').returns @result - - Puppet::Node.expects(:new).with("mynode.domain.com").returns @node - @searcher.find(@request) - end - - it "should add any classes from ldap" do - @result[:classes] = %w[a b c d] - @node.expects(:classes=).with(%w{a b c d}) - @searcher.find(@request) - end - - it "should add all entry attributes as node parameters" do - @result[:parameters] = {"one" => "two", "three" => "four"} - @node.expects(:parameters=).with("one" => "two", "three" => "four") - @searcher.find(@request) - end - - it "should set the node's environment to the environment of the results" do - @result[:environment] = "test" - @node.expects(:environment=).with("test") - @searcher.find(@request) - end - - it "should retain false parameter values" do - @result[:parameters] = {} - @result[:parameters]["one"] = false - @node.expects(:parameters=).with("one" => false) - @searcher.find(@request) - end - - it "should merge the node's facts after the parameters from ldap are assigned" do - # Make sure we've got data to start with, so the parameters are actually set. - @result[:parameters] = {} - @result[:parameters]["one"] = "yay" - - # A hackish way to enforce order. - set = false - @node.expects(:parameters=).with { |*args| set = true } - @node.expects(:fact_merge).with { |*args| raise "Facts were merged before parameters were set" unless set; true } - - @searcher.find(@request) - end - - describe "and a parent node is specified" do - before do - @entry = {:classes => [], :parameters => {}} - @parent = {:classes => [], :parameters => {}} - @parent_parent = {:classes => [], :parameters => {}} - - @searcher.stubs(:name2hash).with{|name, env, mode| name == @name}.returns(@entry) - @searcher.stubs(:name2hash).with{|name, env, mode| name == 'parent'}.returns(@parent) - @searcher.stubs(:name2hash).with{|name, env, mode| name == 'parent_parent'}.returns(@parent_parent) - - @searcher.stubs(:parent_attribute).returns(:parent) - end - - it "should search for the parent node" do - @entry[:parent] = "parent" - - @searcher.find(@request) - end - - it "should fail if the parent cannot be found" do - @entry[:parent] = "parent" - - @searcher.expects(:name2hash).with('parent', 'production', 'parent').returns nil - - proc { @searcher.find(@request) }.should raise_error(Puppet::Error) - end - - it "should add any parent classes to the node's classes" do - @entry[:parent] = "parent" - @entry[:classes] = %w{a b} - - @parent[:classes] = %w{c d} - - @node.expects(:classes=).with(%w{a b c d}) - @searcher.find(@request) - end - - it "should add any parent parameters to the node's parameters" do - @entry[:parent] = "parent" - @entry[:parameters]["one"] = "two" - - @parent[:parameters]["three"] = "four" - - @node.expects(:parameters=).with("one" => "two", "three" => "four") - @searcher.find(@request) - end - - it "should prefer node parameters over parent parameters" do - @entry[:parent] = "parent" - @entry[:parameters]["one"] = "two" - - @parent[:parameters]["one"] = "three" - - @node.expects(:parameters=).with("one" => "two") - @searcher.find(@request) - end - - it "should use the parent's environment if the node has none" do - @entry[:parent] = "parent" - - @parent[:environment] = "parent" - - @node.stubs(:parameters=) - @node.expects(:environment=).with("parent") - @searcher.find(@request) - end - - it "should prefer the node's environment to the parent's" do - @entry[:parent] = "parent" - @entry[:environment] = "child" - - @parent[:environment] = "parent" - - @node.stubs(:parameters=) - @node.expects(:environment=).with("child") - @searcher.find(@request) - end - - it "should recursively look up parent information" do - @entry[:parent] = "parent" - @entry[:parameters]["one"] = "two" - - @parent[:parent] = "parent_parent" - @parent[:parameters]["three"] = "four" - - @parent_parent[:parameters]["five"] = "six" - - @node.expects(:parameters=).with("one" => "two", "three" => "four", "five" => "six") - @searcher.find(@request) - end - - it "should not allow loops in parent declarations" do - @entry[:parent] = "parent" - @parent[:parent] = @name - proc { @searcher.find(@request) }.should raise_error(ArgumentError) - end - end - end - end - - describe "when searching for multiple nodes" do - before :each do - @searcher = Puppet::Node::Ldap.new - @options = {} - @request = stub 'request', :key => "foo", :options => @options - - Puppet::Node::Facts.stubs(:terminus_class).returns :yaml - end - - it "should find all nodes if no arguments are provided" do - @searcher.expects(:ldapsearch).with("(objectclass=puppetClient)") - # LAK:NOTE The search method requires an essentially bogus key. It's - # an API problem that I don't really know how to fix. - @searcher.search @request - end - - describe "and a class is specified" do - it "should find all nodes that are members of that class" do - @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one))") - - @options[:class] = "one" - @searcher.search @request - end - end - - describe "multiple classes are specified" do - it "should find all nodes that are members of all classes" do - @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one)(puppetclass=two))") - @options[:class] = %w{one two} - @searcher.search @request - end - end - - it "should process each found entry" do - # .yields can't be used to yield multiple values :/ - @searcher.expects(:ldapsearch).yields("one") - @searcher.expects(:entry2hash).with("one",nil).returns(:name => "foo") - @searcher.search @request - end - - it "should return a node for each processed entry with the name from the entry" do - @searcher.expects(:ldapsearch).yields("whatever") - @searcher.expects(:entry2hash).with("whatever",nil).returns(:name => "foo") - result = @searcher.search(@request) - result[0].should be_instance_of(Puppet::Node) - result[0].name.should == "foo" - end - - it "should merge each node's facts" do - node = mock 'node' - Puppet::Node.expects(:new).with("foo").returns node - node.expects(:fact_merge) - @searcher.stubs(:ldapsearch).yields("one") - @searcher.stubs(:entry2hash).with("one",nil).returns(:name => "foo") - @searcher.search(@request) - end - - it "should pass the request's fqdn option to entry2hash" do - node = mock 'node' - @options[:fqdn] = :hello - Puppet::Node.stubs(:new).with("foo").returns node - node.stubs(:fact_merge) - @searcher.stubs(:ldapsearch).yields("one") - @searcher.expects(:entry2hash).with("one",:hello).returns(:name => "foo") - @searcher.search(@request) - end - end -end - -describe Puppet::Node::Ldap, " when developing the search query" do - before do - @searcher = Puppet::Node::Ldap.new - end - - it "should return the value of the :ldapclassattrs split on commas as the class attributes" do - Puppet.stubs(:[]).with(:ldapclassattrs).returns("one,two") - @searcher.class_attributes.should == %w{one two} - end - - it "should return nil as the parent attribute if the :ldapparentattr is set to an empty string" do - Puppet.stubs(:[]).with(:ldapparentattr).returns("") - @searcher.parent_attribute.should be_nil - end - - it "should return the value of the :ldapparentattr as the parent attribute" do - Puppet.stubs(:[]).with(:ldapparentattr).returns("pere") - @searcher.parent_attribute.should == "pere" - end - - it "should use the value of the :ldapstring as the search filter" do - Puppet.stubs(:[]).with(:ldapstring).returns("mystring") - @searcher.search_filter("testing").should == "mystring" - end - - it "should replace '%s' with the node name in the search filter if it is present" do - Puppet.stubs(:[]).with(:ldapstring).returns("my%sstring") - @searcher.search_filter("testing").should == "mytestingstring" - end - - it "should not modify the global :ldapstring when replacing '%s' in the search filter" do - filter = mock 'filter' - filter.expects(:include?).with("%s").returns(true) - filter.expects(:gsub).with("%s", "testing").returns("mynewstring") - Puppet.stubs(:[]).with(:ldapstring).returns(filter) - @searcher.search_filter("testing").should == "mynewstring" - end -end - -describe Puppet::Node::Ldap, " when deciding attributes to search for" do - before do - @searcher = Puppet::Node::Ldap.new - end - - it "should use 'nil' if the :ldapattrs setting is 'all'" do - Puppet.stubs(:[]).with(:ldapattrs).returns("all") - @searcher.search_attributes.should be_nil - end - - it "should split the value of :ldapattrs on commas and use the result as the attribute list" do - Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") - @searcher.stubs(:class_attributes).returns([]) - @searcher.stubs(:parent_attribute).returns(nil) - @searcher.search_attributes.should == %w{one two} - end - - it "should add the class attributes to the search attributes if not returning all attributes" do - Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") - @searcher.stubs(:class_attributes).returns(%w{three four}) - @searcher.stubs(:parent_attribute).returns(nil) - # Sort them so i don't have to care about return order - @searcher.search_attributes.sort.should == %w{one two three four}.sort - end - - it "should add the parent attribute to the search attributes if not returning all attributes" do - Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") - @searcher.stubs(:class_attributes).returns([]) - @searcher.stubs(:parent_attribute).returns("parent") - @searcher.search_attributes.sort.should == %w{one two parent}.sort - end - - it "should not add nil parent attributes to the search attributes" do - Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") - @searcher.stubs(:class_attributes).returns([]) - @searcher.stubs(:parent_attribute).returns(nil) - @searcher.search_attributes.should == %w{one two} - end -end diff --git a/spec/unit/indirector/node/memory_spec.rb b/spec/unit/indirector/node/memory_spec.rb new file mode 100755 index 000000000..71e01d4f3 --- /dev/null +++ b/spec/unit/indirector/node/memory_spec.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/node/memory' + +require 'shared_behaviours/memory_terminus' + +describe Puppet::Node::Memory do + before do + @name = "me" + @searcher = Puppet::Node::Memory.new + @instance = stub 'instance', :name => @name + + @request = stub 'request', :key => @name, :instance => @instance + end + + it_should_behave_like "A Memory Terminus" +end diff --git a/spec/unit/indirector/node/memory_spec_spec.rb b/spec/unit/indirector/node/memory_spec_spec.rb deleted file mode 100755 index 71e01d4f3..000000000 --- a/spec/unit/indirector/node/memory_spec_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/node/memory' - -require 'shared_behaviours/memory_terminus' - -describe Puppet::Node::Memory do - before do - @name = "me" - @searcher = Puppet::Node::Memory.new - @instance = stub 'instance', :name => @name - - @request = stub 'request', :key => @name, :instance => @instance - end - - it_should_behave_like "A Memory Terminus" -end diff --git a/spec/unit/indirector/node/plain_spec.rb b/spec/unit/indirector/node/plain_spec.rb new file mode 100755 index 000000000..a595998a1 --- /dev/null +++ b/spec/unit/indirector/node/plain_spec.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/node/plain' + +describe Puppet::Node::Plain do + before do + @searcher = Puppet::Node::Plain.new + end + + it "should call node_merge() on the returned node" do + node = mock 'node' + Puppet::Node.expects(:new).with("mynode").returns(node) + node.expects(:fact_merge) + request = stub 'request', :key => "mynode" + @searcher.find(request) + end +end diff --git a/spec/unit/indirector/node/plain_spec_spec.rb b/spec/unit/indirector/node/plain_spec_spec.rb deleted file mode 100755 index a595998a1..000000000 --- a/spec/unit/indirector/node/plain_spec_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/node/plain' - -describe Puppet::Node::Plain do - before do - @searcher = Puppet::Node::Plain.new - end - - it "should call node_merge() on the returned node" do - node = mock 'node' - Puppet::Node.expects(:new).with("mynode").returns(node) - node.expects(:fact_merge) - request = stub 'request', :key => "mynode" - @searcher.find(request) - end -end diff --git a/spec/unit/indirector/node/rest_spec.rb b/spec/unit/indirector/node/rest_spec.rb new file mode 100755 index 000000000..c00bb98d0 --- /dev/null +++ b/spec/unit/indirector/node/rest_spec.rb @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/node/rest' + +describe Puppet::Node::Rest do + before do + @searcher = Puppet::Node::Rest.new + end + + +end diff --git a/spec/unit/indirector/node/rest_spec_spec.rb b/spec/unit/indirector/node/rest_spec_spec.rb deleted file mode 100755 index c00bb98d0..000000000 --- a/spec/unit/indirector/node/rest_spec_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/node/rest' - -describe Puppet::Node::Rest do - before do - @searcher = Puppet::Node::Rest.new - end - - -end diff --git a/spec/unit/indirector/node/yaml_spec.rb b/spec/unit/indirector/node/yaml_spec.rb new file mode 100755 index 000000000..2e6c14c71 --- /dev/null +++ b/spec/unit/indirector/node/yaml_spec.rb @@ -0,0 +1,25 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/node' +require 'puppet/indirector/node/yaml' + +describe Puppet::Node::Yaml do + it "should be a subclass of the Yaml terminus" do + Puppet::Node::Yaml.superclass.should equal(Puppet::Indirector::Yaml) + end + + it "should have documentation" do + Puppet::Node::Yaml.doc.should_not be_nil + end + + it "should be registered with the configuration store indirection" do + indirection = Puppet::Indirector::Indirection.instance(:node) + Puppet::Node::Yaml.indirection.should equal(indirection) + end + + it "should have its name set to :node" do + Puppet::Node::Yaml.name.should == :yaml + end +end diff --git a/spec/unit/indirector/node/yaml_spec_spec.rb b/spec/unit/indirector/node/yaml_spec_spec.rb deleted file mode 100755 index 2e6c14c71..000000000 --- a/spec/unit/indirector/node/yaml_spec_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/node' -require 'puppet/indirector/node/yaml' - -describe Puppet::Node::Yaml do - it "should be a subclass of the Yaml terminus" do - Puppet::Node::Yaml.superclass.should equal(Puppet::Indirector::Yaml) - end - - it "should have documentation" do - Puppet::Node::Yaml.doc.should_not be_nil - end - - it "should be registered with the configuration store indirection" do - indirection = Puppet::Indirector::Indirection.instance(:node) - Puppet::Node::Yaml.indirection.should equal(indirection) - end - - it "should have its name set to :node" do - Puppet::Node::Yaml.name.should == :yaml - end -end diff --git a/spec/unit/indirector/plain_spec.rb b/spec/unit/indirector/plain_spec.rb new file mode 100755 index 000000000..aca2816f2 --- /dev/null +++ b/spec/unit/indirector/plain_spec.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/indirector/plain' + +describe Puppet::Indirector::Plain do + before do + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @model = mock 'model' + @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model + Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) + + @plain_class = Class.new(Puppet::Indirector::Plain) do + def self.to_s + "Mystuff::Testing" + end + end + + @searcher = @plain_class.new + + @request = stub 'request', :key => "yay" + end + + it "should return return an instance of the indirected model" do + object = mock 'object' + @model.expects(:new).with(@request.key).returns object + @searcher.find(@request).should equal(object) + end +end diff --git a/spec/unit/indirector/plain_spec_spec.rb b/spec/unit/indirector/plain_spec_spec.rb deleted file mode 100755 index aca2816f2..000000000 --- a/spec/unit/indirector/plain_spec_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector/plain' - -describe Puppet::Indirector::Plain do - before do - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @model = mock 'model' - @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model - Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - - @plain_class = Class.new(Puppet::Indirector::Plain) do - def self.to_s - "Mystuff::Testing" - end - end - - @searcher = @plain_class.new - - @request = stub 'request', :key => "yay" - end - - it "should return return an instance of the indirected model" do - object = mock 'object' - @model.expects(:new).with(@request.key).returns object - @searcher.find(@request).should equal(object) - end -end diff --git a/spec/unit/indirector/queue_spec.rb b/spec/unit/indirector/queue_spec.rb new file mode 100755 index 000000000..0748b5817 --- /dev/null +++ b/spec/unit/indirector/queue_spec.rb @@ -0,0 +1,123 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/indirector/queue' + +class Puppet::Indirector::Queue::TestClient +end + +class FooExampleData + attr_accessor :name + + def self.pson_create(pson) + new(pson['data'].to_sym) + end + + def initialize(name = nil) + @name = name if name + end + + def render(format = :pson) + to_pson + end + + def to_pson(*args) + {:type => self.class.to_s, :data => name}.to_pson(*args) + end +end + +describe Puppet::Indirector::Queue do + confine "PSON library is missing; cannot test queueing" => Puppet.features.pson? + + before :each do + @model = mock 'model' + @indirection = stub 'indirection', :name => :my_queue, :register_terminus_type => nil, :model => @model + Puppet::Indirector::Indirection.stubs(:instance).with(:my_queue).returns(@indirection) + @store_class = Class.new(Puppet::Indirector::Queue) do + def self.to_s + 'MyQueue::MyType' + end + end + @store = @store_class.new + + @subject_class = FooExampleData + @subject = @subject_class.new + @subject.name = :me + + Puppet.settings.stubs(:value).returns("bogus setting data") + Puppet.settings.stubs(:value).with(:queue_type).returns(:test_client) + Puppet::Util::Queue.stubs(:queue_type_to_class).with(:test_client).returns(Puppet::Indirector::Queue::TestClient) + + @request = stub 'request', :key => :me, :instance => @subject + end + + it "should require PSON" do + Puppet.features.expects(:pson?).returns false + + lambda { @store_class.new }.should raise_error(ArgumentError) + end + + it 'should use the correct client type and queue' do + @store.queue.should == :my_queue + @store.client.should be_an_instance_of(Puppet::Indirector::Queue::TestClient) + end + + describe "when saving" do + it 'should render the instance using pson' do + @subject.expects(:render).with(:pson) + @store.client.stubs(:send_message) + @store.save(@request) + end + + it "should send the rendered message to the appropriate queue on the client" do + @subject.expects(:render).returns "mypson" + + @store.client.expects(:send_message).with(:my_queue, "mypson") + + @store.save(@request) + end + + it "should catch any exceptions raised" do + @store.client.expects(:send_message).raises ArgumentError + + lambda { @store.save(@request) }.should raise_error(Puppet::Error) + end + end + + describe "when subscribing to the queue" do + before do + @store_class.stubs(:model).returns @model + end + + it "should use the model's Format support to intern the message from pson" do + @model.expects(:convert_from).with(:pson, "mymessage") + + @store_class.client.expects(:subscribe).yields("mymessage") + @store_class.subscribe {|o| o } + end + + it "should yield each interned received message" do + @model.stubs(:convert_from).returns "something" + + @subject_two = @subject_class.new + @subject_two.name = :too + + @store_class.client.expects(:subscribe).with(:my_queue).multiple_yields(@subject, @subject_two) + + received = [] + @store_class.subscribe do |obj| + received.push(obj) + end + + received.should == %w{something something} + end + + it "should log but not propagate errors" do + @store_class.client.expects(:subscribe).yields("foo") + @store_class.expects(:intern).raises ArgumentError + Puppet.expects(:err) + @store_class.subscribe {|o| o } + end + end +end + diff --git a/spec/unit/indirector/queue_spec_spec.rb b/spec/unit/indirector/queue_spec_spec.rb deleted file mode 100755 index 0748b5817..000000000 --- a/spec/unit/indirector/queue_spec_spec.rb +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector/queue' - -class Puppet::Indirector::Queue::TestClient -end - -class FooExampleData - attr_accessor :name - - def self.pson_create(pson) - new(pson['data'].to_sym) - end - - def initialize(name = nil) - @name = name if name - end - - def render(format = :pson) - to_pson - end - - def to_pson(*args) - {:type => self.class.to_s, :data => name}.to_pson(*args) - end -end - -describe Puppet::Indirector::Queue do - confine "PSON library is missing; cannot test queueing" => Puppet.features.pson? - - before :each do - @model = mock 'model' - @indirection = stub 'indirection', :name => :my_queue, :register_terminus_type => nil, :model => @model - Puppet::Indirector::Indirection.stubs(:instance).with(:my_queue).returns(@indirection) - @store_class = Class.new(Puppet::Indirector::Queue) do - def self.to_s - 'MyQueue::MyType' - end - end - @store = @store_class.new - - @subject_class = FooExampleData - @subject = @subject_class.new - @subject.name = :me - - Puppet.settings.stubs(:value).returns("bogus setting data") - Puppet.settings.stubs(:value).with(:queue_type).returns(:test_client) - Puppet::Util::Queue.stubs(:queue_type_to_class).with(:test_client).returns(Puppet::Indirector::Queue::TestClient) - - @request = stub 'request', :key => :me, :instance => @subject - end - - it "should require PSON" do - Puppet.features.expects(:pson?).returns false - - lambda { @store_class.new }.should raise_error(ArgumentError) - end - - it 'should use the correct client type and queue' do - @store.queue.should == :my_queue - @store.client.should be_an_instance_of(Puppet::Indirector::Queue::TestClient) - end - - describe "when saving" do - it 'should render the instance using pson' do - @subject.expects(:render).with(:pson) - @store.client.stubs(:send_message) - @store.save(@request) - end - - it "should send the rendered message to the appropriate queue on the client" do - @subject.expects(:render).returns "mypson" - - @store.client.expects(:send_message).with(:my_queue, "mypson") - - @store.save(@request) - end - - it "should catch any exceptions raised" do - @store.client.expects(:send_message).raises ArgumentError - - lambda { @store.save(@request) }.should raise_error(Puppet::Error) - end - end - - describe "when subscribing to the queue" do - before do - @store_class.stubs(:model).returns @model - end - - it "should use the model's Format support to intern the message from pson" do - @model.expects(:convert_from).with(:pson, "mymessage") - - @store_class.client.expects(:subscribe).yields("mymessage") - @store_class.subscribe {|o| o } - end - - it "should yield each interned received message" do - @model.stubs(:convert_from).returns "something" - - @subject_two = @subject_class.new - @subject_two.name = :too - - @store_class.client.expects(:subscribe).with(:my_queue).multiple_yields(@subject, @subject_two) - - received = [] - @store_class.subscribe do |obj| - received.push(obj) - end - - received.should == %w{something something} - end - - it "should log but not propagate errors" do - @store_class.client.expects(:subscribe).yields("foo") - @store_class.expects(:intern).raises ArgumentError - Puppet.expects(:err) - @store_class.subscribe {|o| o } - end - end -end - diff --git a/spec/unit/indirector/report/processor_spec.rb b/spec/unit/indirector/report/processor_spec.rb new file mode 100755 index 000000000..3ee9b1b5e --- /dev/null +++ b/spec/unit/indirector/report/processor_spec.rb @@ -0,0 +1,81 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-9-23. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/report/processor' + +describe Puppet::Transaction::Report::Processor do + before do + Puppet.settings.stubs(:use).returns(true) + end + + it "should provide a method for saving reports" do + Puppet::Transaction::Report::Processor.new.should respond_to(:save) + end +end + +describe Puppet::Transaction::Report::Processor, " when saving a report" do + before do + Puppet.settings.stubs(:use) + @reporter = Puppet::Transaction::Report::Processor.new + end + + it "should not process the report if reports are set to 'none'" do + Puppet::Reports.expects(:report).never + Puppet.settings.expects(:value).with(:reports).returns("none") + + request = stub 'request', :instance => mock("report") + + @reporter.save(request) + end + + it "should process the report with each configured report type" do + Puppet.settings.stubs(:value).with(:reports).returns("one,two") + @reporter.send(:reports).should == %w{one two} + end +end + +describe Puppet::Transaction::Report::Processor, " when processing a report" do + before do + Puppet.settings.stubs(:value).with(:reports).returns("one") + Puppet.settings.stubs(:use) + @reporter = Puppet::Transaction::Report::Processor.new + + @report_type = mock 'one' + @dup_report = mock 'dupe report' + @dup_report.stubs(:process) + @report = mock 'report' + @report.expects(:dup).returns(@dup_report) + + @request = stub 'request', :instance => @report + + Puppet::Reports.expects(:report).with("one").returns(@report_type) + + @dup_report.expects(:extend).with(@report_type) + end + + # LAK:NOTE This is stupid, because the code is so short it doesn't + # make sense to split it out, which means I just do the same test + # three times so the spec looks right. + it "should process a duplicate of the report, not the original" do + @reporter.save(@request) + end + + it "should extend the report with the report type's module" do + @reporter.save(@request) + end + + it "should call the report type's :process method" do + @dup_report.expects(:process) + @reporter.save(@request) + end + + it "should not raise exceptions" do + Puppet.settings.stubs(:value).with(:trace).returns(false) + @dup_report.expects(:process).raises(ArgumentError) + proc { @reporter.save(@request) }.should_not raise_error + end +end diff --git a/spec/unit/indirector/report/processor_spec_spec.rb b/spec/unit/indirector/report/processor_spec_spec.rb deleted file mode 100755 index 3ee9b1b5e..000000000 --- a/spec/unit/indirector/report/processor_spec_spec.rb +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-9-23. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/report/processor' - -describe Puppet::Transaction::Report::Processor do - before do - Puppet.settings.stubs(:use).returns(true) - end - - it "should provide a method for saving reports" do - Puppet::Transaction::Report::Processor.new.should respond_to(:save) - end -end - -describe Puppet::Transaction::Report::Processor, " when saving a report" do - before do - Puppet.settings.stubs(:use) - @reporter = Puppet::Transaction::Report::Processor.new - end - - it "should not process the report if reports are set to 'none'" do - Puppet::Reports.expects(:report).never - Puppet.settings.expects(:value).with(:reports).returns("none") - - request = stub 'request', :instance => mock("report") - - @reporter.save(request) - end - - it "should process the report with each configured report type" do - Puppet.settings.stubs(:value).with(:reports).returns("one,two") - @reporter.send(:reports).should == %w{one two} - end -end - -describe Puppet::Transaction::Report::Processor, " when processing a report" do - before do - Puppet.settings.stubs(:value).with(:reports).returns("one") - Puppet.settings.stubs(:use) - @reporter = Puppet::Transaction::Report::Processor.new - - @report_type = mock 'one' - @dup_report = mock 'dupe report' - @dup_report.stubs(:process) - @report = mock 'report' - @report.expects(:dup).returns(@dup_report) - - @request = stub 'request', :instance => @report - - Puppet::Reports.expects(:report).with("one").returns(@report_type) - - @dup_report.expects(:extend).with(@report_type) - end - - # LAK:NOTE This is stupid, because the code is so short it doesn't - # make sense to split it out, which means I just do the same test - # three times so the spec looks right. - it "should process a duplicate of the report, not the original" do - @reporter.save(@request) - end - - it "should extend the report with the report type's module" do - @reporter.save(@request) - end - - it "should call the report type's :process method" do - @dup_report.expects(:process) - @reporter.save(@request) - end - - it "should not raise exceptions" do - Puppet.settings.stubs(:value).with(:trace).returns(false) - @dup_report.expects(:process).raises(ArgumentError) - proc { @reporter.save(@request) }.should_not raise_error - end -end diff --git a/spec/unit/indirector/report/rest_spec.rb b/spec/unit/indirector/report/rest_spec.rb new file mode 100755 index 000000000..1f71eb357 --- /dev/null +++ b/spec/unit/indirector/report/rest_spec.rb @@ -0,0 +1,28 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/report/rest' + +describe Puppet::Transaction::Report::Rest do + it "should be a subclass of Puppet::Indirector::REST" do + Puppet::Transaction::Report::Rest.superclass.should equal(Puppet::Indirector::REST) + end + + it "should use the :report_server setting in preference to :reportserver" do + Puppet.settings[:reportserver] = "reportserver" + Puppet.settings[:report_server] = "report_server" + Puppet::Transaction::Report::Rest.server.should == "report_server" + end + + it "should use the :report_server setting in preference to :server" do + Puppet.settings[:server] = "server" + Puppet.settings[:report_server] = "report_server" + Puppet::Transaction::Report::Rest.server.should == "report_server" + end + + it "should have a value for report_server and report_port" do + Puppet::Transaction::Report::Rest.server.should_not be_nil + Puppet::Transaction::Report::Rest.port.should_not be_nil + end +end diff --git a/spec/unit/indirector/report/rest_spec_spec.rb b/spec/unit/indirector/report/rest_spec_spec.rb deleted file mode 100755 index 1f71eb357..000000000 --- a/spec/unit/indirector/report/rest_spec_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/report/rest' - -describe Puppet::Transaction::Report::Rest do - it "should be a subclass of Puppet::Indirector::REST" do - Puppet::Transaction::Report::Rest.superclass.should equal(Puppet::Indirector::REST) - end - - it "should use the :report_server setting in preference to :reportserver" do - Puppet.settings[:reportserver] = "reportserver" - Puppet.settings[:report_server] = "report_server" - Puppet::Transaction::Report::Rest.server.should == "report_server" - end - - it "should use the :report_server setting in preference to :server" do - Puppet.settings[:server] = "server" - Puppet.settings[:report_server] = "report_server" - Puppet::Transaction::Report::Rest.server.should == "report_server" - end - - it "should have a value for report_server and report_port" do - Puppet::Transaction::Report::Rest.server.should_not be_nil - Puppet::Transaction::Report::Rest.port.should_not be_nil - end -end diff --git a/spec/unit/indirector/request_spec.rb b/spec/unit/indirector/request_spec.rb new file mode 100755 index 000000000..58226397e --- /dev/null +++ b/spec/unit/indirector/request_spec.rb @@ -0,0 +1,304 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/indirector/request' + +describe Puppet::Indirector::Request do + describe "when initializing" do + it "should require an indirection name, a key, and a method" do + lambda { Puppet::Indirector::Request.new }.should raise_error(ArgumentError) + end + + it "should always convert the indirection name to a symbol" do + Puppet::Indirector::Request.new("ind", :method, "mykey").indirection_name.should == :ind + end + + it "should use provided value as the key if it is a string" do + Puppet::Indirector::Request.new(:ind, :method, "mykey").key.should == "mykey" + end + + it "should use provided value as the key if it is a symbol" do + Puppet::Indirector::Request.new(:ind, :method, :mykey).key.should == :mykey + end + + it "should use the name of the provided instance as its key if an instance is provided as the key instead of a string" do + instance = mock 'instance', :name => "mykey" + request = Puppet::Indirector::Request.new(:ind, :method, instance) + request.key.should == "mykey" + request.instance.should equal(instance) + end + + it "should support options specified as a hash" do + lambda { Puppet::Indirector::Request.new(:ind, :method, :key, :one => :two) }.should_not raise_error(ArgumentError) + end + + it "should support nil options" do + lambda { Puppet::Indirector::Request.new(:ind, :method, :key, nil) }.should_not raise_error(ArgumentError) + end + + it "should support unspecified options" do + lambda { Puppet::Indirector::Request.new(:ind, :method, :key) }.should_not raise_error(ArgumentError) + end + + it "should use an empty options hash if nil was provided" do + Puppet::Indirector::Request.new(:ind, :method, :key, nil).options.should == {} + end + + it "should default to a nil node" do + Puppet::Indirector::Request.new(:ind, :method, :key, nil).node.should be_nil + end + + it "should set its node attribute if provided in the options" do + Puppet::Indirector::Request.new(:ind, :method, :key, :node => "foo.com").node.should == "foo.com" + end + + it "should default to a nil ip" do + Puppet::Indirector::Request.new(:ind, :method, :key, nil).ip.should be_nil + end + + it "should set its ip attribute if provided in the options" do + Puppet::Indirector::Request.new(:ind, :method, :key, :ip => "192.168.0.1").ip.should == "192.168.0.1" + end + + it "should default to being unauthenticated" do + Puppet::Indirector::Request.new(:ind, :method, :key, nil).should_not be_authenticated + end + + it "should set be marked authenticated if configured in the options" do + Puppet::Indirector::Request.new(:ind, :method, :key, :authenticated => "eh").should be_authenticated + end + + it "should keep its options as a hash even if a node is specified" do + Puppet::Indirector::Request.new(:ind, :method, :key, :node => "eh").options.should be_instance_of(Hash) + end + + it "should keep its options as a hash even if another option is specified" do + Puppet::Indirector::Request.new(:ind, :method, :key, :foo => "bar").options.should be_instance_of(Hash) + end + + it "should treat options other than :ip, :node, and :authenticated as options rather than attributes" do + Puppet::Indirector::Request.new(:ind, :method, :key, :server => "bar").options[:server].should == "bar" + end + + it "should normalize options to use symbols as keys" do + Puppet::Indirector::Request.new(:ind, :method, :key, "foo" => "bar").options[:foo].should == "bar" + end + + describe "and the request key is a URI" do + describe "and the URI is a 'file' URI" do + before do + @request = Puppet::Indirector::Request.new(:ind, :method, "file:///my/file with spaces") + end + + it "should set the request key to the unescaped full file path" do + @request.key.should == "/my/file with spaces" + end + + it "should not set the protocol" do + @request.protocol.should be_nil + end + + it "should not set the port" do + @request.port.should be_nil + end + + it "should not set the server" do + @request.server.should be_nil + end + end + + it "should set the protocol to the URI scheme" do + Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").protocol.should == "http" + end + + it "should set the server if a server is provided" do + Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").server.should == "host" + end + + it "should set the server and port if both are provided" do + Puppet::Indirector::Request.new(:ind, :method, "http://host:543/stuff").port.should == 543 + end + + it "should default to the masterport if the URI scheme is 'puppet'" do + Puppet.settings.expects(:value).with(:masterport).returns "321" + Puppet::Indirector::Request.new(:ind, :method, "puppet://host/stuff").port.should == 321 + end + + it "should use the provided port if the URI scheme is not 'puppet'" do + Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").port.should == 80 + end + + it "should set the request key to the unescaped key part path from the URI" do + Puppet::Indirector::Request.new(:ind, :method, "http://host/environment/terminus/stuff with spaces").key.should == "stuff with spaces" + end + + it "should set the :uri attribute to the full URI" do + Puppet::Indirector::Request.new(:ind, :method, "http:///stuff").uri.should == "http:///stuff" + end + end + + it "should allow indication that it should not read a cached instance" do + Puppet::Indirector::Request.new(:ind, :method, :key, :ignore_cache => true).should be_ignore_cache + end + + it "should default to not ignoring the cache" do + Puppet::Indirector::Request.new(:ind, :method, :key).should_not be_ignore_cache + end + + it "should allow indication that it should not not read an instance from the terminus" do + Puppet::Indirector::Request.new(:ind, :method, :key, :ignore_terminus => true).should be_ignore_terminus + end + + it "should default to not ignoring the terminus" do + Puppet::Indirector::Request.new(:ind, :method, :key).should_not be_ignore_terminus + end + end + + it "should look use the Indirection class to return the appropriate indirection" do + ind = mock 'indirection' + Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind + request = Puppet::Indirector::Request.new(:myind, :method, :key) + + request.indirection.should equal(ind) + end + + it "should use its indirection to look up the appropriate model" do + ind = mock 'indirection' + Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind + request = Puppet::Indirector::Request.new(:myind, :method, :key) + + ind.expects(:model).returns "mymodel" + + request.model.should == "mymodel" + end + + it "should fail intelligently when asked to find a model but the indirection cannot be found" do + Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns nil + request = Puppet::Indirector::Request.new(:myind, :method, :key) + + lambda { request.model }.should raise_error(ArgumentError) + end + + it "should have a method for determining if the request is plural or singular" do + Puppet::Indirector::Request.new(:myind, :method, :key).should respond_to(:plural?) + end + + it "should be considered plural if the method is 'search'" do + Puppet::Indirector::Request.new(:myind, :search, :key).should be_plural + end + + it "should not be considered plural if the method is not 'search'" do + Puppet::Indirector::Request.new(:myind, :find, :key).should_not be_plural + end + + it "should use its uri, if it has one, as its string representation" do + Puppet::Indirector::Request.new(:myind, :find, "foo://bar/baz").to_s.should == "foo://bar/baz" + end + + it "should use its indirection name and key, if it has no uri, as its string representation" do + Puppet::Indirector::Request.new(:myind, :find, "key") == "/myind/key" + end + + it "should be able to return the URI-escaped key" do + Puppet::Indirector::Request.new(:myind, :find, "my key").escaped_key.should == URI.escape("my key") + end + + it "should have an environment accessor" do + Puppet::Indirector::Request.new(:myind, :find, "my key", :environment => "foo").should respond_to(:environment) + end + + it "should set its environment to an environment instance when a string is specified as its environment" do + Puppet::Indirector::Request.new(:myind, :find, "my key", :environment => "foo").environment.should == Puppet::Node::Environment.new("foo") + end + + it "should use any passed in environment instances as its environment" do + env = Puppet::Node::Environment.new("foo") + Puppet::Indirector::Request.new(:myind, :find, "my key", :environment => env).environment.should equal(env) + end + + it "should use the default environment when none is provided" do + Puppet::Indirector::Request.new(:myind, :find, "my key" ).environment.should equal(Puppet::Node::Environment.new) + end + + it "should support converting its options to a hash" do + Puppet::Indirector::Request.new(:myind, :find, "my key" ).should respond_to(:to_hash) + end + + it "should include all of its attributes when its options are converted to a hash" do + Puppet::Indirector::Request.new(:myind, :find, "my key", :node => 'foo').to_hash[:node].should == 'foo' + end + + describe "when building a query string from its options" do + before do + @request = Puppet::Indirector::Request.new(:myind, :find, "my key") + end + + it "should return an empty query string if there are no options" do + @request.stubs(:options).returns nil + @request.query_string.should == "" + end + + it "should return an empty query string if the options are empty" do + @request.stubs(:options).returns({}) + @request.query_string.should == "" + end + + it "should prefix the query string with '?'" do + @request.stubs(:options).returns(:one => "two") + @request.query_string.should =~ /^\?/ + end + + it "should include all options in the query string, separated by '&'" do + @request.stubs(:options).returns(:one => "two", :three => "four") + @request.query_string.sub(/^\?/, '').split("&").sort.should == %w{one=two three=four}.sort + end + + it "should ignore nil options" do + @request.stubs(:options).returns(:one => "two", :three => nil) + @request.query_string.should_not be_include("three") + end + + it "should convert 'true' option values into strings" do + @request.stubs(:options).returns(:one => true) + @request.query_string.should == "?one=true" + end + + it "should convert 'false' option values into strings" do + @request.stubs(:options).returns(:one => false) + @request.query_string.should == "?one=false" + end + + it "should convert to a string all option values that are integers" do + @request.stubs(:options).returns(:one => 50) + @request.query_string.should == "?one=50" + end + + it "should convert to a string all option values that are floating point numbers" do + @request.stubs(:options).returns(:one => 1.2) + @request.query_string.should == "?one=1.2" + end + + it "should CGI-escape all option values that are strings" do + escaping = CGI.escape("one two") + @request.stubs(:options).returns(:one => "one two") + @request.query_string.should == "?one=#{escaping}" + end + + it "should YAML-dump and CGI-escape arrays" do + escaping = CGI.escape(YAML.dump(%w{one two})) + @request.stubs(:options).returns(:one => %w{one two}) + @request.query_string.should == "?one=#{escaping}" + end + + it "should convert to a string and CGI-escape all option values that are symbols" do + escaping = CGI.escape("sym bol") + @request.stubs(:options).returns(:one => :"sym bol") + @request.query_string.should == "?one=#{escaping}" + end + + it "should fail if options other than booleans or strings are provided" do + @request.stubs(:options).returns(:one => {:one => :two}) + lambda { @request.query_string }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/unit/indirector/request_spec_spec.rb b/spec/unit/indirector/request_spec_spec.rb deleted file mode 100755 index 58226397e..000000000 --- a/spec/unit/indirector/request_spec_spec.rb +++ /dev/null @@ -1,304 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector/request' - -describe Puppet::Indirector::Request do - describe "when initializing" do - it "should require an indirection name, a key, and a method" do - lambda { Puppet::Indirector::Request.new }.should raise_error(ArgumentError) - end - - it "should always convert the indirection name to a symbol" do - Puppet::Indirector::Request.new("ind", :method, "mykey").indirection_name.should == :ind - end - - it "should use provided value as the key if it is a string" do - Puppet::Indirector::Request.new(:ind, :method, "mykey").key.should == "mykey" - end - - it "should use provided value as the key if it is a symbol" do - Puppet::Indirector::Request.new(:ind, :method, :mykey).key.should == :mykey - end - - it "should use the name of the provided instance as its key if an instance is provided as the key instead of a string" do - instance = mock 'instance', :name => "mykey" - request = Puppet::Indirector::Request.new(:ind, :method, instance) - request.key.should == "mykey" - request.instance.should equal(instance) - end - - it "should support options specified as a hash" do - lambda { Puppet::Indirector::Request.new(:ind, :method, :key, :one => :two) }.should_not raise_error(ArgumentError) - end - - it "should support nil options" do - lambda { Puppet::Indirector::Request.new(:ind, :method, :key, nil) }.should_not raise_error(ArgumentError) - end - - it "should support unspecified options" do - lambda { Puppet::Indirector::Request.new(:ind, :method, :key) }.should_not raise_error(ArgumentError) - end - - it "should use an empty options hash if nil was provided" do - Puppet::Indirector::Request.new(:ind, :method, :key, nil).options.should == {} - end - - it "should default to a nil node" do - Puppet::Indirector::Request.new(:ind, :method, :key, nil).node.should be_nil - end - - it "should set its node attribute if provided in the options" do - Puppet::Indirector::Request.new(:ind, :method, :key, :node => "foo.com").node.should == "foo.com" - end - - it "should default to a nil ip" do - Puppet::Indirector::Request.new(:ind, :method, :key, nil).ip.should be_nil - end - - it "should set its ip attribute if provided in the options" do - Puppet::Indirector::Request.new(:ind, :method, :key, :ip => "192.168.0.1").ip.should == "192.168.0.1" - end - - it "should default to being unauthenticated" do - Puppet::Indirector::Request.new(:ind, :method, :key, nil).should_not be_authenticated - end - - it "should set be marked authenticated if configured in the options" do - Puppet::Indirector::Request.new(:ind, :method, :key, :authenticated => "eh").should be_authenticated - end - - it "should keep its options as a hash even if a node is specified" do - Puppet::Indirector::Request.new(:ind, :method, :key, :node => "eh").options.should be_instance_of(Hash) - end - - it "should keep its options as a hash even if another option is specified" do - Puppet::Indirector::Request.new(:ind, :method, :key, :foo => "bar").options.should be_instance_of(Hash) - end - - it "should treat options other than :ip, :node, and :authenticated as options rather than attributes" do - Puppet::Indirector::Request.new(:ind, :method, :key, :server => "bar").options[:server].should == "bar" - end - - it "should normalize options to use symbols as keys" do - Puppet::Indirector::Request.new(:ind, :method, :key, "foo" => "bar").options[:foo].should == "bar" - end - - describe "and the request key is a URI" do - describe "and the URI is a 'file' URI" do - before do - @request = Puppet::Indirector::Request.new(:ind, :method, "file:///my/file with spaces") - end - - it "should set the request key to the unescaped full file path" do - @request.key.should == "/my/file with spaces" - end - - it "should not set the protocol" do - @request.protocol.should be_nil - end - - it "should not set the port" do - @request.port.should be_nil - end - - it "should not set the server" do - @request.server.should be_nil - end - end - - it "should set the protocol to the URI scheme" do - Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").protocol.should == "http" - end - - it "should set the server if a server is provided" do - Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").server.should == "host" - end - - it "should set the server and port if both are provided" do - Puppet::Indirector::Request.new(:ind, :method, "http://host:543/stuff").port.should == 543 - end - - it "should default to the masterport if the URI scheme is 'puppet'" do - Puppet.settings.expects(:value).with(:masterport).returns "321" - Puppet::Indirector::Request.new(:ind, :method, "puppet://host/stuff").port.should == 321 - end - - it "should use the provided port if the URI scheme is not 'puppet'" do - Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").port.should == 80 - end - - it "should set the request key to the unescaped key part path from the URI" do - Puppet::Indirector::Request.new(:ind, :method, "http://host/environment/terminus/stuff with spaces").key.should == "stuff with spaces" - end - - it "should set the :uri attribute to the full URI" do - Puppet::Indirector::Request.new(:ind, :method, "http:///stuff").uri.should == "http:///stuff" - end - end - - it "should allow indication that it should not read a cached instance" do - Puppet::Indirector::Request.new(:ind, :method, :key, :ignore_cache => true).should be_ignore_cache - end - - it "should default to not ignoring the cache" do - Puppet::Indirector::Request.new(:ind, :method, :key).should_not be_ignore_cache - end - - it "should allow indication that it should not not read an instance from the terminus" do - Puppet::Indirector::Request.new(:ind, :method, :key, :ignore_terminus => true).should be_ignore_terminus - end - - it "should default to not ignoring the terminus" do - Puppet::Indirector::Request.new(:ind, :method, :key).should_not be_ignore_terminus - end - end - - it "should look use the Indirection class to return the appropriate indirection" do - ind = mock 'indirection' - Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind - request = Puppet::Indirector::Request.new(:myind, :method, :key) - - request.indirection.should equal(ind) - end - - it "should use its indirection to look up the appropriate model" do - ind = mock 'indirection' - Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind - request = Puppet::Indirector::Request.new(:myind, :method, :key) - - ind.expects(:model).returns "mymodel" - - request.model.should == "mymodel" - end - - it "should fail intelligently when asked to find a model but the indirection cannot be found" do - Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns nil - request = Puppet::Indirector::Request.new(:myind, :method, :key) - - lambda { request.model }.should raise_error(ArgumentError) - end - - it "should have a method for determining if the request is plural or singular" do - Puppet::Indirector::Request.new(:myind, :method, :key).should respond_to(:plural?) - end - - it "should be considered plural if the method is 'search'" do - Puppet::Indirector::Request.new(:myind, :search, :key).should be_plural - end - - it "should not be considered plural if the method is not 'search'" do - Puppet::Indirector::Request.new(:myind, :find, :key).should_not be_plural - end - - it "should use its uri, if it has one, as its string representation" do - Puppet::Indirector::Request.new(:myind, :find, "foo://bar/baz").to_s.should == "foo://bar/baz" - end - - it "should use its indirection name and key, if it has no uri, as its string representation" do - Puppet::Indirector::Request.new(:myind, :find, "key") == "/myind/key" - end - - it "should be able to return the URI-escaped key" do - Puppet::Indirector::Request.new(:myind, :find, "my key").escaped_key.should == URI.escape("my key") - end - - it "should have an environment accessor" do - Puppet::Indirector::Request.new(:myind, :find, "my key", :environment => "foo").should respond_to(:environment) - end - - it "should set its environment to an environment instance when a string is specified as its environment" do - Puppet::Indirector::Request.new(:myind, :find, "my key", :environment => "foo").environment.should == Puppet::Node::Environment.new("foo") - end - - it "should use any passed in environment instances as its environment" do - env = Puppet::Node::Environment.new("foo") - Puppet::Indirector::Request.new(:myind, :find, "my key", :environment => env).environment.should equal(env) - end - - it "should use the default environment when none is provided" do - Puppet::Indirector::Request.new(:myind, :find, "my key" ).environment.should equal(Puppet::Node::Environment.new) - end - - it "should support converting its options to a hash" do - Puppet::Indirector::Request.new(:myind, :find, "my key" ).should respond_to(:to_hash) - end - - it "should include all of its attributes when its options are converted to a hash" do - Puppet::Indirector::Request.new(:myind, :find, "my key", :node => 'foo').to_hash[:node].should == 'foo' - end - - describe "when building a query string from its options" do - before do - @request = Puppet::Indirector::Request.new(:myind, :find, "my key") - end - - it "should return an empty query string if there are no options" do - @request.stubs(:options).returns nil - @request.query_string.should == "" - end - - it "should return an empty query string if the options are empty" do - @request.stubs(:options).returns({}) - @request.query_string.should == "" - end - - it "should prefix the query string with '?'" do - @request.stubs(:options).returns(:one => "two") - @request.query_string.should =~ /^\?/ - end - - it "should include all options in the query string, separated by '&'" do - @request.stubs(:options).returns(:one => "two", :three => "four") - @request.query_string.sub(/^\?/, '').split("&").sort.should == %w{one=two three=four}.sort - end - - it "should ignore nil options" do - @request.stubs(:options).returns(:one => "two", :three => nil) - @request.query_string.should_not be_include("three") - end - - it "should convert 'true' option values into strings" do - @request.stubs(:options).returns(:one => true) - @request.query_string.should == "?one=true" - end - - it "should convert 'false' option values into strings" do - @request.stubs(:options).returns(:one => false) - @request.query_string.should == "?one=false" - end - - it "should convert to a string all option values that are integers" do - @request.stubs(:options).returns(:one => 50) - @request.query_string.should == "?one=50" - end - - it "should convert to a string all option values that are floating point numbers" do - @request.stubs(:options).returns(:one => 1.2) - @request.query_string.should == "?one=1.2" - end - - it "should CGI-escape all option values that are strings" do - escaping = CGI.escape("one two") - @request.stubs(:options).returns(:one => "one two") - @request.query_string.should == "?one=#{escaping}" - end - - it "should YAML-dump and CGI-escape arrays" do - escaping = CGI.escape(YAML.dump(%w{one two})) - @request.stubs(:options).returns(:one => %w{one two}) - @request.query_string.should == "?one=#{escaping}" - end - - it "should convert to a string and CGI-escape all option values that are symbols" do - escaping = CGI.escape("sym bol") - @request.stubs(:options).returns(:one => :"sym bol") - @request.query_string.should == "?one=#{escaping}" - end - - it "should fail if options other than booleans or strings are provided" do - @request.stubs(:options).returns(:one => {:one => :two}) - lambda { @request.query_string }.should raise_error(ArgumentError) - end - end -end diff --git a/spec/unit/indirector/resource/ral_spec.rb b/spec/unit/indirector/resource/ral_spec.rb new file mode 100644 index 000000000..c3dc8d8cf --- /dev/null +++ b/spec/unit/indirector/resource/ral_spec.rb @@ -0,0 +1,129 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "Puppet::Resource::Ral" do + describe "find" do + before do + @request = stub 'request', :key => "user/root" + end + + it "should find an existing instance" do + my_resource = stub "my user resource" + + wrong_instance = stub "wrong user", :name => "bob" + my_instance = stub "my user", :name => "root", :to_resource => my_resource + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ wrong_instance, my_instance, wrong_instance ]) + Puppet::Resource::Ral.new.find(@request).should == my_resource + end + + it "if there is no instance, it should create one" do + wrong_instance = stub "wrong user", :name => "bob" + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ wrong_instance, wrong_instance ]) + result = Puppet::Resource::Ral.new.find(@request) + result.should be_is_a(Puppet::Resource) + result.title.should == "root" + end + end + + describe "search" do + before do + @request = stub 'request', :key => "user/", :options => {} + end + + it "should convert ral resources into regular resources" do + my_resource = stub "my user resource" + my_instance = stub "my user", :name => "root", :to_resource => my_resource + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ my_instance ]) + Puppet::Resource::Ral.new.search(@request).should == [my_resource] + end + + it "should filter results by name if there's a name in the key" do + my_resource = stub "my user resource" + my_resource.stubs(:to_resource).returns(my_resource) + my_resource.stubs(:[]).with(:name).returns("root") + + wrong_resource = stub "wrong resource" + wrong_resource.stubs(:to_resource).returns(wrong_resource) + wrong_resource.stubs(:[]).with(:name).returns("bad") + + my_instance = stub "my user", :to_resource => my_resource + wrong_instance = stub "wrong user", :to_resource => wrong_resource + + @request = stub 'request', :key => "user/root", :options => {} + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) + Puppet::Resource::Ral.new.search(@request).should == [my_resource] + end + + it "should filter results by query parameters" do + wrong_resource = stub "my user resource" + wrong_resource.stubs(:to_resource).returns(wrong_resource) + wrong_resource.stubs(:[]).with(:name).returns("root") + + my_resource = stub "wrong resource" + my_resource.stubs(:to_resource).returns(my_resource) + my_resource.stubs(:[]).with(:name).returns("bob") + + my_instance = stub "my user", :to_resource => my_resource + wrong_instance = stub "wrong user", :to_resource => wrong_resource + + @request = stub 'request', :key => "user/", :options => {:name => "bob"} + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) + Puppet::Resource::Ral.new.search(@request).should == [my_resource] + end + + it "should return sorted results" do + a_resource = stub "alice resource" + a_resource.stubs(:to_resource).returns(a_resource) + a_resource.stubs(:title).returns("alice") + + b_resource = stub "bob resource" + b_resource.stubs(:to_resource).returns(b_resource) + b_resource.stubs(:title).returns("bob") + + a_instance = stub "alice user", :to_resource => a_resource + b_instance = stub "bob user", :to_resource => b_resource + + @request = stub 'request', :key => "user/", :options => {} + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ b_instance, a_instance ]) + Puppet::Resource::Ral.new.search(@request).should == [a_resource, b_resource] + end + end + + describe "save" do + before do + @rebuilt_res = stub 'rebuilt instance' + @ral_res = stub 'ral resource', :to_resource => @rebuilt_res + @instance = stub 'instance', :to_ral => @ral_res + @request = stub 'request', :key => "user/", :instance => @instance + @catalog = stub 'catalog' + + Puppet::Resource::Catalog.stubs(:new).returns(@catalog) + @catalog.stubs(:apply) + @catalog.stubs(:add_resource) + end + + it "should apply a new catalog with a ral object in it" do + Puppet::Resource::Catalog.expects(:new).returns(@catalog) + @catalog.expects(:add_resource).with(@ral_res) + @catalog.expects(:apply) + Puppet::Resource::Ral.new.save(@request) + end + + it "should return a regular resource that used to be the ral resource" do + Puppet::Resource::Ral.new.save(@request).should == @rebuilt_res + end + end +end diff --git a/spec/unit/indirector/resource/ral_spec_spec.rb b/spec/unit/indirector/resource/ral_spec_spec.rb deleted file mode 100644 index c3dc8d8cf..000000000 --- a/spec/unit/indirector/resource/ral_spec_spec.rb +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "Puppet::Resource::Ral" do - describe "find" do - before do - @request = stub 'request', :key => "user/root" - end - - it "should find an existing instance" do - my_resource = stub "my user resource" - - wrong_instance = stub "wrong user", :name => "bob" - my_instance = stub "my user", :name => "root", :to_resource => my_resource - - require 'puppet/type/user' - Puppet::Type::User.expects(:instances).returns([ wrong_instance, my_instance, wrong_instance ]) - Puppet::Resource::Ral.new.find(@request).should == my_resource - end - - it "if there is no instance, it should create one" do - wrong_instance = stub "wrong user", :name => "bob" - - require 'puppet/type/user' - Puppet::Type::User.expects(:instances).returns([ wrong_instance, wrong_instance ]) - result = Puppet::Resource::Ral.new.find(@request) - result.should be_is_a(Puppet::Resource) - result.title.should == "root" - end - end - - describe "search" do - before do - @request = stub 'request', :key => "user/", :options => {} - end - - it "should convert ral resources into regular resources" do - my_resource = stub "my user resource" - my_instance = stub "my user", :name => "root", :to_resource => my_resource - - require 'puppet/type/user' - Puppet::Type::User.expects(:instances).returns([ my_instance ]) - Puppet::Resource::Ral.new.search(@request).should == [my_resource] - end - - it "should filter results by name if there's a name in the key" do - my_resource = stub "my user resource" - my_resource.stubs(:to_resource).returns(my_resource) - my_resource.stubs(:[]).with(:name).returns("root") - - wrong_resource = stub "wrong resource" - wrong_resource.stubs(:to_resource).returns(wrong_resource) - wrong_resource.stubs(:[]).with(:name).returns("bad") - - my_instance = stub "my user", :to_resource => my_resource - wrong_instance = stub "wrong user", :to_resource => wrong_resource - - @request = stub 'request', :key => "user/root", :options => {} - - require 'puppet/type/user' - Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) - Puppet::Resource::Ral.new.search(@request).should == [my_resource] - end - - it "should filter results by query parameters" do - wrong_resource = stub "my user resource" - wrong_resource.stubs(:to_resource).returns(wrong_resource) - wrong_resource.stubs(:[]).with(:name).returns("root") - - my_resource = stub "wrong resource" - my_resource.stubs(:to_resource).returns(my_resource) - my_resource.stubs(:[]).with(:name).returns("bob") - - my_instance = stub "my user", :to_resource => my_resource - wrong_instance = stub "wrong user", :to_resource => wrong_resource - - @request = stub 'request', :key => "user/", :options => {:name => "bob"} - - require 'puppet/type/user' - Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) - Puppet::Resource::Ral.new.search(@request).should == [my_resource] - end - - it "should return sorted results" do - a_resource = stub "alice resource" - a_resource.stubs(:to_resource).returns(a_resource) - a_resource.stubs(:title).returns("alice") - - b_resource = stub "bob resource" - b_resource.stubs(:to_resource).returns(b_resource) - b_resource.stubs(:title).returns("bob") - - a_instance = stub "alice user", :to_resource => a_resource - b_instance = stub "bob user", :to_resource => b_resource - - @request = stub 'request', :key => "user/", :options => {} - - require 'puppet/type/user' - Puppet::Type::User.expects(:instances).returns([ b_instance, a_instance ]) - Puppet::Resource::Ral.new.search(@request).should == [a_resource, b_resource] - end - end - - describe "save" do - before do - @rebuilt_res = stub 'rebuilt instance' - @ral_res = stub 'ral resource', :to_resource => @rebuilt_res - @instance = stub 'instance', :to_ral => @ral_res - @request = stub 'request', :key => "user/", :instance => @instance - @catalog = stub 'catalog' - - Puppet::Resource::Catalog.stubs(:new).returns(@catalog) - @catalog.stubs(:apply) - @catalog.stubs(:add_resource) - end - - it "should apply a new catalog with a ral object in it" do - Puppet::Resource::Catalog.expects(:new).returns(@catalog) - @catalog.expects(:add_resource).with(@ral_res) - @catalog.expects(:apply) - Puppet::Resource::Ral.new.save(@request) - end - - it "should return a regular resource that used to be the ral resource" do - Puppet::Resource::Ral.new.save(@request).should == @rebuilt_res - end - end -end diff --git a/spec/unit/indirector/resource/rest_spec.rb b/spec/unit/indirector/resource/rest_spec.rb new file mode 100755 index 000000000..d5f2a9d46 --- /dev/null +++ b/spec/unit/indirector/resource/rest_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/indirector/resource/rest' + +describe Puppet::Resource::Rest do + it "should be a sublcass of Puppet::Indirector::REST" do + Puppet::Resource::Rest.superclass.should equal(Puppet::Indirector::REST) + end +end diff --git a/spec/unit/indirector/resource/rest_spec_spec.rb b/spec/unit/indirector/resource/rest_spec_spec.rb deleted file mode 100755 index d5f2a9d46..000000000 --- a/spec/unit/indirector/resource/rest_spec_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/indirector/resource/rest' - -describe Puppet::Resource::Rest do - it "should be a sublcass of Puppet::Indirector::REST" do - Puppet::Resource::Rest.superclass.should equal(Puppet::Indirector::REST) - end -end diff --git a/spec/unit/indirector/resource_type/parser_spec.rb b/spec/unit/indirector/resource_type/parser_spec.rb new file mode 100755 index 000000000..331ca1c9b --- /dev/null +++ b/spec/unit/indirector/resource_type/parser_spec.rb @@ -0,0 +1,75 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/resource_type/parser' + +describe Puppet::Indirector::ResourceType::Parser do + before do + @terminus = Puppet::Indirector::ResourceType::Parser.new + @request = Puppet::Indirector::Request.new(:resource_type, :find, "foo") + @krt = Puppet::Resource::TypeCollection.new(@request.environment) + @request.environment.stubs(:known_resource_types).returns @krt + end + + it "should be registered with the resource_type indirection" do + Puppet::Indirector::Terminus.terminus_class(:resource_type, :parser).should equal(Puppet::Indirector::ResourceType::Parser) + end + + describe "when finding" do + it "should use the request's environment's list of known resource types" do + @request.environment.known_resource_types.expects(:hostclass).returns nil + + @terminus.find(@request) + end + + it "should return any found type" do + type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) + + @terminus.find(@request).should == type + end + + it "should return nil if no type can be found" do + @terminus.find(@request).should be_nil + end + + it "should prefer definitions to nodes" do + type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) + node = @krt.add(Puppet::Resource::Type.new(:node, "foo")) + + @terminus.find(@request).should == type + end + end + + describe "when searching" do + before do + @request.key = "*" + end + + it "should use the request's environment's list of known resource types" do + @request.environment.known_resource_types.expects(:hostclasses).returns({}) + + @terminus.search(@request) + end + + it "should fail if anyther other than '*' was provided as the search key" do + @request.key = "foo*" + lambda { @terminus.search(@request) }.should raise_error(ArgumentError) + end + + it "should return all known types" do + type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) + node = @krt.add(Puppet::Resource::Type.new(:node, "bar")) + define = @krt.add(Puppet::Resource::Type.new(:definition, "baz")) + + result = @terminus.search(@request) + result.should be_include(type) + result.should be_include(node) + result.should be_include(define) + end + + it "should return nil if no types can be found" do + @terminus.search(@request).should be_nil + end + end +end diff --git a/spec/unit/indirector/resource_type/parser_spec_spec.rb b/spec/unit/indirector/resource_type/parser_spec_spec.rb deleted file mode 100755 index 331ca1c9b..000000000 --- a/spec/unit/indirector/resource_type/parser_spec_spec.rb +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/resource_type/parser' - -describe Puppet::Indirector::ResourceType::Parser do - before do - @terminus = Puppet::Indirector::ResourceType::Parser.new - @request = Puppet::Indirector::Request.new(:resource_type, :find, "foo") - @krt = Puppet::Resource::TypeCollection.new(@request.environment) - @request.environment.stubs(:known_resource_types).returns @krt - end - - it "should be registered with the resource_type indirection" do - Puppet::Indirector::Terminus.terminus_class(:resource_type, :parser).should equal(Puppet::Indirector::ResourceType::Parser) - end - - describe "when finding" do - it "should use the request's environment's list of known resource types" do - @request.environment.known_resource_types.expects(:hostclass).returns nil - - @terminus.find(@request) - end - - it "should return any found type" do - type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) - - @terminus.find(@request).should == type - end - - it "should return nil if no type can be found" do - @terminus.find(@request).should be_nil - end - - it "should prefer definitions to nodes" do - type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) - node = @krt.add(Puppet::Resource::Type.new(:node, "foo")) - - @terminus.find(@request).should == type - end - end - - describe "when searching" do - before do - @request.key = "*" - end - - it "should use the request's environment's list of known resource types" do - @request.environment.known_resource_types.expects(:hostclasses).returns({}) - - @terminus.search(@request) - end - - it "should fail if anyther other than '*' was provided as the search key" do - @request.key = "foo*" - lambda { @terminus.search(@request) }.should raise_error(ArgumentError) - end - - it "should return all known types" do - type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) - node = @krt.add(Puppet::Resource::Type.new(:node, "bar")) - define = @krt.add(Puppet::Resource::Type.new(:definition, "baz")) - - result = @terminus.search(@request) - result.should be_include(type) - result.should be_include(node) - result.should be_include(define) - end - - it "should return nil if no types can be found" do - @terminus.search(@request).should be_nil - end - end -end diff --git a/spec/unit/indirector/resource_type/rest_spec.rb b/spec/unit/indirector/resource_type/rest_spec.rb new file mode 100755 index 000000000..28196d53a --- /dev/null +++ b/spec/unit/indirector/resource_type/rest_spec.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/resource_type/rest' + +describe Puppet::Indirector::ResourceType::Rest do + it "should be registered with the resource_type indirection" do + Puppet::Indirector::Terminus.terminus_class(:resource_type, :rest).should equal(Puppet::Indirector::ResourceType::Rest) + end + + it "should be a subclass of Puppet::Indirector::Rest" do + Puppet::Indirector::ResourceType::Rest.superclass.should == Puppet::Indirector::REST + end +end diff --git a/spec/unit/indirector/resource_type/rest_spec_spec.rb b/spec/unit/indirector/resource_type/rest_spec_spec.rb deleted file mode 100755 index 28196d53a..000000000 --- a/spec/unit/indirector/resource_type/rest_spec_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/resource_type/rest' - -describe Puppet::Indirector::ResourceType::Rest do - it "should be registered with the resource_type indirection" do - Puppet::Indirector::Terminus.terminus_class(:resource_type, :rest).should equal(Puppet::Indirector::ResourceType::Rest) - end - - it "should be a subclass of Puppet::Indirector::Rest" do - Puppet::Indirector::ResourceType::Rest.superclass.should == Puppet::Indirector::REST - end -end diff --git a/spec/unit/indirector/rest_spec.rb b/spec/unit/indirector/rest_spec.rb new file mode 100755 index 000000000..668158c53 --- /dev/null +++ b/spec/unit/indirector/rest_spec.rb @@ -0,0 +1,455 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/indirector/rest' + +describe "a REST http call", :shared => true do + it "should accept a path" do + lambda { @search.send(@method, *@arguments) }.should_not raise_error(ArgumentError) + end + + it "should require a path" do + lambda { @searcher.send(@method) }.should raise_error(ArgumentError) + end + + it "should return the results of deserializing the response to the request" do + conn = mock 'connection' + conn.stubs(:put).returns @response + conn.stubs(:delete).returns @response + conn.stubs(:get).returns @response + Puppet::Network::HttpPool.stubs(:http_instance).returns conn + + @searcher.expects(:deserialize).with(@response).returns "myobject" + + @searcher.send(@method, *@arguments).should == 'myobject' + end +end + +describe Puppet::Indirector::REST do + before do + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @model = stub('model', :supported_formats => %w{}, :convert_from => nil) + @instance = stub('model instance', :name= => nil) + @indirection = stub('indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model) + Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) + + @rest_class = Class.new(Puppet::Indirector::REST) do + def self.to_s + "This::Is::A::Test::Class" + end + end + + @response = stub('mock response', :body => 'result', :code => "200") + @response.stubs(:[]).with('content-type').returns "text/plain" + @response.stubs(:[]).with('content-encoding').returns nil + + @searcher = @rest_class.new + @searcher.stubs(:model).returns @model + end + + it "should include the v1 REST API module" do + Puppet::Indirector::REST.ancestors.should be_include(Puppet::Network::HTTP::API::V1) + end + + it "should have a method for specifying what setting a subclass should use to retrieve its server" do + @rest_class.should respond_to(:use_server_setting) + end + + it "should use any specified setting to pick the server" do + @rest_class.expects(:server_setting).returns :servset + Puppet.settings.expects(:value).with(:servset).returns "myserver" + @rest_class.server.should == "myserver" + end + + it "should default to :server for the server setting" do + @rest_class.expects(:server_setting).returns nil + Puppet.settings.expects(:value).with(:server).returns "myserver" + @rest_class.server.should == "myserver" + end + + it "should have a method for specifying what setting a subclass should use to retrieve its port" do + @rest_class.should respond_to(:use_port_setting) + end + + it "should use any specified setting to pick the port" do + @rest_class.expects(:port_setting).returns :servset + Puppet.settings.expects(:value).with(:servset).returns "321" + @rest_class.port.should == 321 + end + + it "should default to :port for the port setting" do + @rest_class.expects(:port_setting).returns nil + Puppet.settings.expects(:value).with(:masterport).returns "543" + @rest_class.port.should == 543 + end + + describe "when deserializing responses" do + it "should return nil if the response code is 404" do + response = mock 'response' + response.expects(:code).returns "404" + + @searcher.deserialize(response).should be_nil + end + + [300,400,403,405,500,501,502,503,504].each { |rc| + describe "when the response code is #{rc}" do + before :each do + @model.expects(:convert_from).never + + @response = mock 'response' + @response.stubs(:code).returns rc.to_s + @response.stubs(:[]).with('content-encoding').returns nil + @response.stubs(:message).returns "There was a problem (header)" + end + + it "should fail" do + @response.stubs(:body).returns nil + lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError) + end + + it "should take the error message from the body, if present" do + @response.stubs(:body).returns "There was a problem (body)" + lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (body)") + end + + it "should take the error message from the response header if the body is empty" do + @response.stubs(:body).returns "" + lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (header)") + end + + it "should take the error message from the response header if the body is absent" do + @response.stubs(:body).returns nil + lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (header)") + end + + describe "and with http compression" do + it "should uncompress the body" do + @response.stubs(:body).returns("compressed body") + @searcher.expects(:uncompress_body).with(@response).returns("uncompressed") + lambda { @searcher.deserialize(@response) }.should raise_error { |e| e.message =~ /uncompressed/ } + end + end + end + } + + it "should return the results of converting from the format specified by the content-type header if the response code is in the 200s" do + @model.expects(:convert_from).with("myformat", "mydata").returns "myobject" + + response = mock 'response' + response.stubs(:[]).with("content-type").returns "myformat" + response.stubs(:[]).with("content-encoding").returns nil + response.stubs(:body).returns "mydata" + response.stubs(:code).returns "200" + + @searcher.deserialize(response).should == "myobject" + end + + it "should convert and return multiple instances if the return code is in the 200s and 'multiple' is specified" do + @model.expects(:convert_from_multiple).with("myformat", "mydata").returns "myobjects" + + response = mock 'response' + response.stubs(:[]).with("content-type").returns "myformat" + response.stubs(:[]).with("content-encoding").returns nil + response.stubs(:body).returns "mydata" + response.stubs(:code).returns "200" + + @searcher.deserialize(response, true).should == "myobjects" + end + + it "should strip the content-type header to keep only the mime-type" do + @model.expects(:convert_from).with("text/plain", "mydata").returns "myobject" + + response = mock 'response' + response.stubs(:[]).with("content-type").returns "text/plain; charset=utf-8" + response.stubs(:[]).with("content-encoding").returns nil + response.stubs(:body).returns "mydata" + response.stubs(:code).returns "200" + + @searcher.deserialize(response) + end + + it "should uncompress the body" do + @model.expects(:convert_from).with("myformat", "uncompressed mydata").returns "myobject" + + response = mock 'response' + response.stubs(:[]).with("content-type").returns "myformat" + response.stubs(:body).returns "compressed mydata" + response.stubs(:code).returns "200" + + @searcher.expects(:uncompress_body).with(response).returns("uncompressed mydata") + + @searcher.deserialize(response).should == "myobject" + end + end + + describe "when creating an HTTP client" do + before do + Puppet.settings.stubs(:value).returns("rest_testing") + end + + it "should use the class's server and port if the indirection request provides neither" do + @request = stub 'request', :key => "foo", :server => nil, :port => nil + @searcher.class.expects(:port).returns 321 + @searcher.class.expects(:server).returns "myserver" + Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" + @searcher.network(@request).should == "myconn" + end + + it "should use the server from the indirection request if one is present" do + @request = stub 'request', :key => "foo", :server => "myserver", :port => nil + @searcher.class.stubs(:port).returns 321 + Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" + @searcher.network(@request).should == "myconn" + end + + it "should use the port from the indirection request if one is present" do + @request = stub 'request', :key => "foo", :server => nil, :port => 321 + @searcher.class.stubs(:server).returns "myserver" + Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" + @searcher.network(@request).should == "myconn" + end + end + + describe "when doing a find" do + before :each do + @connection = stub('mock http connection', :get => @response) + @searcher.stubs(:network).returns(@connection) # neuter the network connection + + # Use a key with spaces, so we can test escaping + @request = Puppet::Indirector::Request.new(:foo, :find, "foo bar") + end + + it "should call the GET http method on a network connection" do + @searcher.expects(:network).returns @connection + @connection.expects(:get).returns @response + @searcher.find(@request) + end + + it "should deserialize and return the http response, setting name" do + @connection.expects(:get).returns @response + + instance = stub 'object' + instance.expects(:name=) + @searcher.expects(:deserialize).with(@response).returns instance + + @searcher.find(@request).should == instance + end + + it "should deserialize and return the http response, and not require name=" do + @connection.expects(:get).returns @response + + instance = stub 'object' + @searcher.expects(:deserialize).with(@response).returns instance + + @searcher.find(@request).should == instance + end + + + it "should use the URI generated by the Handler module" do + @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" + @connection.expects(:get).with { |path, args| path == "/my/uri" }.returns(@response) + @searcher.find(@request) + end + + it "should provide an Accept header containing the list of supported formats joined with commas" do + @connection.expects(:get).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) + + @searcher.model.expects(:supported_formats).returns %w{supported formats} + @searcher.find(@request) + end + + it "should add Accept-Encoding header" do + @searcher.expects(:add_accept_encoding).returns({"accept-encoding" => "gzip"}) + + @connection.expects(:get).with { |path, args| args["accept-encoding"] == "gzip" }.returns(@response) + @searcher.find(@request) + end + + it "should deserialize and return the network response" do + @searcher.expects(:deserialize).with(@response).returns @instance + @searcher.find(@request).should equal(@instance) + end + + it "should set the name of the resulting instance to the asked-for name" do + @searcher.expects(:deserialize).with(@response).returns @instance + @instance.expects(:name=).with "foo bar" + @searcher.find(@request) + end + + it "should generate an error when result data deserializes fails" do + @searcher.expects(:deserialize).raises(ArgumentError) + lambda { @searcher.find(@request) }.should raise_error(ArgumentError) + end + end + + describe "when doing a search" do + before :each do + @connection = stub('mock http connection', :get => @response) + @searcher.stubs(:network).returns(@connection) # neuter the network connection + + @model.stubs(:convert_from_multiple) + + @request = Puppet::Indirector::Request.new(:foo, :search, "foo bar") + end + + it "should call the GET http method on a network connection" do + @searcher.expects(:network).returns @connection + @connection.expects(:get).returns @response + @searcher.search(@request) + end + + it "should deserialize as multiple instances and return the http response" do + @connection.expects(:get).returns @response + @searcher.expects(:deserialize).with(@response, true).returns "myobject" + + @searcher.search(@request).should == 'myobject' + end + + it "should use the URI generated by the Handler module" do + @searcher.expects(:indirection2uri).with(@request).returns "/mys/uri" + @connection.expects(:get).with { |path, args| path == "/mys/uri" }.returns(@response) + @searcher.search(@request) + end + + it "should provide an Accept header containing the list of supported formats joined with commas" do + @connection.expects(:get).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) + + @searcher.model.expects(:supported_formats).returns %w{supported formats} + @searcher.search(@request) + end + + it "should return an empty array if serialization returns nil" do + @model.stubs(:convert_from_multiple).returns nil + + @searcher.search(@request).should == [] + end + + it "should generate an error when result data deserializes fails" do + @searcher.expects(:deserialize).raises(ArgumentError) + lambda { @searcher.search(@request) }.should raise_error(ArgumentError) + end + end + + describe "when doing a destroy" do + before :each do + @connection = stub('mock http connection', :delete => @response) + @searcher.stubs(:network).returns(@connection) # neuter the network connection + + @request = Puppet::Indirector::Request.new(:foo, :destroy, "foo bar") + end + + it "should call the DELETE http method on a network connection" do + @searcher.expects(:network).returns @connection + @connection.expects(:delete).returns @response + @searcher.destroy(@request) + end + + it "should fail if any options are provided, since DELETE apparently does not support query options" do + @request.stubs(:options).returns(:one => "two", :three => "four") + + lambda { @searcher.destroy(@request) }.should raise_error(ArgumentError) + end + + it "should deserialize and return the http response" do + @connection.expects(:delete).returns @response + @searcher.expects(:deserialize).with(@response).returns "myobject" + + @searcher.destroy(@request).should == 'myobject' + end + + it "should use the URI generated by the Handler module" do + @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" + @connection.expects(:delete).with { |path, args| path == "/my/uri" }.returns(@response) + @searcher.destroy(@request) + end + + it "should not include the query string" do + @connection.stubs(:delete).returns @response + @searcher.destroy(@request) + end + + it "should provide an Accept header containing the list of supported formats joined with commas" do + @connection.expects(:delete).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) + + @searcher.model.expects(:supported_formats).returns %w{supported formats} + @searcher.destroy(@request) + end + + it "should deserialize and return the network response" do + @searcher.expects(:deserialize).with(@response).returns @instance + @searcher.destroy(@request).should equal(@instance) + end + + it "should generate an error when result data deserializes fails" do + @searcher.expects(:deserialize).raises(ArgumentError) + lambda { @searcher.destroy(@request) }.should raise_error(ArgumentError) + end + end + + describe "when doing a save" do + before :each do + @connection = stub('mock http connection', :put => @response) + @searcher.stubs(:network).returns(@connection) # neuter the network connection + + @instance = stub 'instance', :render => "mydata", :mime => "mime" + @request = Puppet::Indirector::Request.new(:foo, :save, "foo bar") + @request.instance = @instance + end + + it "should call the PUT http method on a network connection" do + @searcher.expects(:network).returns @connection + @connection.expects(:put).returns @response + @searcher.save(@request) + end + + it "should fail if any options are provided, since DELETE apparently does not support query options" do + @request.stubs(:options).returns(:one => "two", :three => "four") + + lambda { @searcher.save(@request) }.should raise_error(ArgumentError) + end + + it "should use the URI generated by the Handler module" do + @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" + @connection.expects(:put).with { |path, args| path == "/my/uri" }.returns(@response) + @searcher.save(@request) + end + + it "should serialize the instance using the default format and pass the result as the body of the request" do + @instance.expects(:render).returns "serial_instance" + @connection.expects(:put).with { |path, data, args| data == "serial_instance" }.returns @response + + @searcher.save(@request) + end + + it "should deserialize and return the http response" do + @connection.expects(:put).returns @response + @searcher.expects(:deserialize).with(@response).returns "myobject" + + @searcher.save(@request).should == 'myobject' + end + + it "should provide an Accept header containing the list of supported formats joined with commas" do + @connection.expects(:put).with { |path, data, args| args["Accept"] == "supported, formats" }.returns(@response) + + @searcher.model.expects(:supported_formats).returns %w{supported formats} + @searcher.save(@request) + end + + it "should provide a Content-Type header containing the mime-type of the sent object" do + @connection.expects(:put).with { |path, data, args| args['Content-Type'] == "mime" }.returns(@response) + + @instance.expects(:mime).returns "mime" + @searcher.save(@request) + end + + it "should deserialize and return the network response" do + @searcher.expects(:deserialize).with(@response).returns @instance + @searcher.save(@request).should equal(@instance) + end + + it "should generate an error when result data deserializes fails" do + @searcher.expects(:deserialize).raises(ArgumentError) + lambda { @searcher.save(@request) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/unit/indirector/rest_spec_spec.rb b/spec/unit/indirector/rest_spec_spec.rb deleted file mode 100755 index 668158c53..000000000 --- a/spec/unit/indirector/rest_spec_spec.rb +++ /dev/null @@ -1,455 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector/rest' - -describe "a REST http call", :shared => true do - it "should accept a path" do - lambda { @search.send(@method, *@arguments) }.should_not raise_error(ArgumentError) - end - - it "should require a path" do - lambda { @searcher.send(@method) }.should raise_error(ArgumentError) - end - - it "should return the results of deserializing the response to the request" do - conn = mock 'connection' - conn.stubs(:put).returns @response - conn.stubs(:delete).returns @response - conn.stubs(:get).returns @response - Puppet::Network::HttpPool.stubs(:http_instance).returns conn - - @searcher.expects(:deserialize).with(@response).returns "myobject" - - @searcher.send(@method, *@arguments).should == 'myobject' - end -end - -describe Puppet::Indirector::REST do - before do - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @model = stub('model', :supported_formats => %w{}, :convert_from => nil) - @instance = stub('model instance', :name= => nil) - @indirection = stub('indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model) - Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - - @rest_class = Class.new(Puppet::Indirector::REST) do - def self.to_s - "This::Is::A::Test::Class" - end - end - - @response = stub('mock response', :body => 'result', :code => "200") - @response.stubs(:[]).with('content-type').returns "text/plain" - @response.stubs(:[]).with('content-encoding').returns nil - - @searcher = @rest_class.new - @searcher.stubs(:model).returns @model - end - - it "should include the v1 REST API module" do - Puppet::Indirector::REST.ancestors.should be_include(Puppet::Network::HTTP::API::V1) - end - - it "should have a method for specifying what setting a subclass should use to retrieve its server" do - @rest_class.should respond_to(:use_server_setting) - end - - it "should use any specified setting to pick the server" do - @rest_class.expects(:server_setting).returns :servset - Puppet.settings.expects(:value).with(:servset).returns "myserver" - @rest_class.server.should == "myserver" - end - - it "should default to :server for the server setting" do - @rest_class.expects(:server_setting).returns nil - Puppet.settings.expects(:value).with(:server).returns "myserver" - @rest_class.server.should == "myserver" - end - - it "should have a method for specifying what setting a subclass should use to retrieve its port" do - @rest_class.should respond_to(:use_port_setting) - end - - it "should use any specified setting to pick the port" do - @rest_class.expects(:port_setting).returns :servset - Puppet.settings.expects(:value).with(:servset).returns "321" - @rest_class.port.should == 321 - end - - it "should default to :port for the port setting" do - @rest_class.expects(:port_setting).returns nil - Puppet.settings.expects(:value).with(:masterport).returns "543" - @rest_class.port.should == 543 - end - - describe "when deserializing responses" do - it "should return nil if the response code is 404" do - response = mock 'response' - response.expects(:code).returns "404" - - @searcher.deserialize(response).should be_nil - end - - [300,400,403,405,500,501,502,503,504].each { |rc| - describe "when the response code is #{rc}" do - before :each do - @model.expects(:convert_from).never - - @response = mock 'response' - @response.stubs(:code).returns rc.to_s - @response.stubs(:[]).with('content-encoding').returns nil - @response.stubs(:message).returns "There was a problem (header)" - end - - it "should fail" do - @response.stubs(:body).returns nil - lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError) - end - - it "should take the error message from the body, if present" do - @response.stubs(:body).returns "There was a problem (body)" - lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (body)") - end - - it "should take the error message from the response header if the body is empty" do - @response.stubs(:body).returns "" - lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (header)") - end - - it "should take the error message from the response header if the body is absent" do - @response.stubs(:body).returns nil - lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (header)") - end - - describe "and with http compression" do - it "should uncompress the body" do - @response.stubs(:body).returns("compressed body") - @searcher.expects(:uncompress_body).with(@response).returns("uncompressed") - lambda { @searcher.deserialize(@response) }.should raise_error { |e| e.message =~ /uncompressed/ } - end - end - end - } - - it "should return the results of converting from the format specified by the content-type header if the response code is in the 200s" do - @model.expects(:convert_from).with("myformat", "mydata").returns "myobject" - - response = mock 'response' - response.stubs(:[]).with("content-type").returns "myformat" - response.stubs(:[]).with("content-encoding").returns nil - response.stubs(:body).returns "mydata" - response.stubs(:code).returns "200" - - @searcher.deserialize(response).should == "myobject" - end - - it "should convert and return multiple instances if the return code is in the 200s and 'multiple' is specified" do - @model.expects(:convert_from_multiple).with("myformat", "mydata").returns "myobjects" - - response = mock 'response' - response.stubs(:[]).with("content-type").returns "myformat" - response.stubs(:[]).with("content-encoding").returns nil - response.stubs(:body).returns "mydata" - response.stubs(:code).returns "200" - - @searcher.deserialize(response, true).should == "myobjects" - end - - it "should strip the content-type header to keep only the mime-type" do - @model.expects(:convert_from).with("text/plain", "mydata").returns "myobject" - - response = mock 'response' - response.stubs(:[]).with("content-type").returns "text/plain; charset=utf-8" - response.stubs(:[]).with("content-encoding").returns nil - response.stubs(:body).returns "mydata" - response.stubs(:code).returns "200" - - @searcher.deserialize(response) - end - - it "should uncompress the body" do - @model.expects(:convert_from).with("myformat", "uncompressed mydata").returns "myobject" - - response = mock 'response' - response.stubs(:[]).with("content-type").returns "myformat" - response.stubs(:body).returns "compressed mydata" - response.stubs(:code).returns "200" - - @searcher.expects(:uncompress_body).with(response).returns("uncompressed mydata") - - @searcher.deserialize(response).should == "myobject" - end - end - - describe "when creating an HTTP client" do - before do - Puppet.settings.stubs(:value).returns("rest_testing") - end - - it "should use the class's server and port if the indirection request provides neither" do - @request = stub 'request', :key => "foo", :server => nil, :port => nil - @searcher.class.expects(:port).returns 321 - @searcher.class.expects(:server).returns "myserver" - Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" - @searcher.network(@request).should == "myconn" - end - - it "should use the server from the indirection request if one is present" do - @request = stub 'request', :key => "foo", :server => "myserver", :port => nil - @searcher.class.stubs(:port).returns 321 - Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" - @searcher.network(@request).should == "myconn" - end - - it "should use the port from the indirection request if one is present" do - @request = stub 'request', :key => "foo", :server => nil, :port => 321 - @searcher.class.stubs(:server).returns "myserver" - Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" - @searcher.network(@request).should == "myconn" - end - end - - describe "when doing a find" do - before :each do - @connection = stub('mock http connection', :get => @response) - @searcher.stubs(:network).returns(@connection) # neuter the network connection - - # Use a key with spaces, so we can test escaping - @request = Puppet::Indirector::Request.new(:foo, :find, "foo bar") - end - - it "should call the GET http method on a network connection" do - @searcher.expects(:network).returns @connection - @connection.expects(:get).returns @response - @searcher.find(@request) - end - - it "should deserialize and return the http response, setting name" do - @connection.expects(:get).returns @response - - instance = stub 'object' - instance.expects(:name=) - @searcher.expects(:deserialize).with(@response).returns instance - - @searcher.find(@request).should == instance - end - - it "should deserialize and return the http response, and not require name=" do - @connection.expects(:get).returns @response - - instance = stub 'object' - @searcher.expects(:deserialize).with(@response).returns instance - - @searcher.find(@request).should == instance - end - - - it "should use the URI generated by the Handler module" do - @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" - @connection.expects(:get).with { |path, args| path == "/my/uri" }.returns(@response) - @searcher.find(@request) - end - - it "should provide an Accept header containing the list of supported formats joined with commas" do - @connection.expects(:get).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) - - @searcher.model.expects(:supported_formats).returns %w{supported formats} - @searcher.find(@request) - end - - it "should add Accept-Encoding header" do - @searcher.expects(:add_accept_encoding).returns({"accept-encoding" => "gzip"}) - - @connection.expects(:get).with { |path, args| args["accept-encoding"] == "gzip" }.returns(@response) - @searcher.find(@request) - end - - it "should deserialize and return the network response" do - @searcher.expects(:deserialize).with(@response).returns @instance - @searcher.find(@request).should equal(@instance) - end - - it "should set the name of the resulting instance to the asked-for name" do - @searcher.expects(:deserialize).with(@response).returns @instance - @instance.expects(:name=).with "foo bar" - @searcher.find(@request) - end - - it "should generate an error when result data deserializes fails" do - @searcher.expects(:deserialize).raises(ArgumentError) - lambda { @searcher.find(@request) }.should raise_error(ArgumentError) - end - end - - describe "when doing a search" do - before :each do - @connection = stub('mock http connection', :get => @response) - @searcher.stubs(:network).returns(@connection) # neuter the network connection - - @model.stubs(:convert_from_multiple) - - @request = Puppet::Indirector::Request.new(:foo, :search, "foo bar") - end - - it "should call the GET http method on a network connection" do - @searcher.expects(:network).returns @connection - @connection.expects(:get).returns @response - @searcher.search(@request) - end - - it "should deserialize as multiple instances and return the http response" do - @connection.expects(:get).returns @response - @searcher.expects(:deserialize).with(@response, true).returns "myobject" - - @searcher.search(@request).should == 'myobject' - end - - it "should use the URI generated by the Handler module" do - @searcher.expects(:indirection2uri).with(@request).returns "/mys/uri" - @connection.expects(:get).with { |path, args| path == "/mys/uri" }.returns(@response) - @searcher.search(@request) - end - - it "should provide an Accept header containing the list of supported formats joined with commas" do - @connection.expects(:get).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) - - @searcher.model.expects(:supported_formats).returns %w{supported formats} - @searcher.search(@request) - end - - it "should return an empty array if serialization returns nil" do - @model.stubs(:convert_from_multiple).returns nil - - @searcher.search(@request).should == [] - end - - it "should generate an error when result data deserializes fails" do - @searcher.expects(:deserialize).raises(ArgumentError) - lambda { @searcher.search(@request) }.should raise_error(ArgumentError) - end - end - - describe "when doing a destroy" do - before :each do - @connection = stub('mock http connection', :delete => @response) - @searcher.stubs(:network).returns(@connection) # neuter the network connection - - @request = Puppet::Indirector::Request.new(:foo, :destroy, "foo bar") - end - - it "should call the DELETE http method on a network connection" do - @searcher.expects(:network).returns @connection - @connection.expects(:delete).returns @response - @searcher.destroy(@request) - end - - it "should fail if any options are provided, since DELETE apparently does not support query options" do - @request.stubs(:options).returns(:one => "two", :three => "four") - - lambda { @searcher.destroy(@request) }.should raise_error(ArgumentError) - end - - it "should deserialize and return the http response" do - @connection.expects(:delete).returns @response - @searcher.expects(:deserialize).with(@response).returns "myobject" - - @searcher.destroy(@request).should == 'myobject' - end - - it "should use the URI generated by the Handler module" do - @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" - @connection.expects(:delete).with { |path, args| path == "/my/uri" }.returns(@response) - @searcher.destroy(@request) - end - - it "should not include the query string" do - @connection.stubs(:delete).returns @response - @searcher.destroy(@request) - end - - it "should provide an Accept header containing the list of supported formats joined with commas" do - @connection.expects(:delete).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) - - @searcher.model.expects(:supported_formats).returns %w{supported formats} - @searcher.destroy(@request) - end - - it "should deserialize and return the network response" do - @searcher.expects(:deserialize).with(@response).returns @instance - @searcher.destroy(@request).should equal(@instance) - end - - it "should generate an error when result data deserializes fails" do - @searcher.expects(:deserialize).raises(ArgumentError) - lambda { @searcher.destroy(@request) }.should raise_error(ArgumentError) - end - end - - describe "when doing a save" do - before :each do - @connection = stub('mock http connection', :put => @response) - @searcher.stubs(:network).returns(@connection) # neuter the network connection - - @instance = stub 'instance', :render => "mydata", :mime => "mime" - @request = Puppet::Indirector::Request.new(:foo, :save, "foo bar") - @request.instance = @instance - end - - it "should call the PUT http method on a network connection" do - @searcher.expects(:network).returns @connection - @connection.expects(:put).returns @response - @searcher.save(@request) - end - - it "should fail if any options are provided, since DELETE apparently does not support query options" do - @request.stubs(:options).returns(:one => "two", :three => "four") - - lambda { @searcher.save(@request) }.should raise_error(ArgumentError) - end - - it "should use the URI generated by the Handler module" do - @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" - @connection.expects(:put).with { |path, args| path == "/my/uri" }.returns(@response) - @searcher.save(@request) - end - - it "should serialize the instance using the default format and pass the result as the body of the request" do - @instance.expects(:render).returns "serial_instance" - @connection.expects(:put).with { |path, data, args| data == "serial_instance" }.returns @response - - @searcher.save(@request) - end - - it "should deserialize and return the http response" do - @connection.expects(:put).returns @response - @searcher.expects(:deserialize).with(@response).returns "myobject" - - @searcher.save(@request).should == 'myobject' - end - - it "should provide an Accept header containing the list of supported formats joined with commas" do - @connection.expects(:put).with { |path, data, args| args["Accept"] == "supported, formats" }.returns(@response) - - @searcher.model.expects(:supported_formats).returns %w{supported formats} - @searcher.save(@request) - end - - it "should provide a Content-Type header containing the mime-type of the sent object" do - @connection.expects(:put).with { |path, data, args| args['Content-Type'] == "mime" }.returns(@response) - - @instance.expects(:mime).returns "mime" - @searcher.save(@request) - end - - it "should deserialize and return the network response" do - @searcher.expects(:deserialize).with(@response).returns @instance - @searcher.save(@request).should equal(@instance) - end - - it "should generate an error when result data deserializes fails" do - @searcher.expects(:deserialize).raises(ArgumentError) - lambda { @searcher.save(@request) }.should raise_error(ArgumentError) - end - end -end diff --git a/spec/unit/indirector/run/local_spec.rb b/spec/unit/indirector/run/local_spec.rb new file mode 100644 index 000000000..face61d5c --- /dev/null +++ b/spec/unit/indirector/run/local_spec.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/run/local' + +describe Puppet::Run::Local do + it "should be a sublcass of Puppet::Indirector::Code" do + Puppet::Run::Local.superclass.should equal(Puppet::Indirector::Code) + end + + it "should call runner.run on save and return the runner" do + runner = Puppet::Run.new + runner.stubs(:run).returns(runner) + + request = Puppet::Indirector::Request.new(:indirection, :save, "anything") + request.instance = runner = Puppet::Run.new + Puppet::Run::Local.new.save(request).should == runner + end +end diff --git a/spec/unit/indirector/run/local_spec_spec.rb b/spec/unit/indirector/run/local_spec_spec.rb deleted file mode 100644 index face61d5c..000000000 --- a/spec/unit/indirector/run/local_spec_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/run/local' - -describe Puppet::Run::Local do - it "should be a sublcass of Puppet::Indirector::Code" do - Puppet::Run::Local.superclass.should equal(Puppet::Indirector::Code) - end - - it "should call runner.run on save and return the runner" do - runner = Puppet::Run.new - runner.stubs(:run).returns(runner) - - request = Puppet::Indirector::Request.new(:indirection, :save, "anything") - request.instance = runner = Puppet::Run.new - Puppet::Run::Local.new.save(request).should == runner - end -end diff --git a/spec/unit/indirector/run/rest_spec.rb b/spec/unit/indirector/run/rest_spec.rb new file mode 100755 index 000000000..ee976ed9f --- /dev/null +++ b/spec/unit/indirector/run/rest_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/indirector/run/rest' + +describe Puppet::Run::Rest do + it "should be a sublcass of Puppet::Indirector::REST" do + Puppet::Run::Rest.superclass.should equal(Puppet::Indirector::REST) + end +end diff --git a/spec/unit/indirector/run/rest_spec_spec.rb b/spec/unit/indirector/run/rest_spec_spec.rb deleted file mode 100755 index ee976ed9f..000000000 --- a/spec/unit/indirector/run/rest_spec_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/indirector/run/rest' - -describe Puppet::Run::Rest do - it "should be a sublcass of Puppet::Indirector::REST" do - Puppet::Run::Rest.superclass.should equal(Puppet::Indirector::REST) - end -end diff --git a/spec/unit/indirector/ssl_file_spec.rb b/spec/unit/indirector/ssl_file_spec.rb new file mode 100755 index 000000000..8f46409fe --- /dev/null +++ b/spec/unit/indirector/ssl_file_spec.rb @@ -0,0 +1,281 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-10. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/indirector/ssl_file' + +describe Puppet::Indirector::SslFile do + before do + @model = mock 'model' + @indirection = stub 'indirection', :name => :testing, :model => @model + Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection) + @file_class = Class.new(Puppet::Indirector::SslFile) do + def self.to_s + "Testing::Mytype" + end + end + + @setting = :mydir + @file_class.store_in @setting + @path = "/my/directory" + Puppet.settings.stubs(:value).with(:noop).returns(false) + Puppet.settings.stubs(:value).with(@setting).returns(@path) + Puppet.settings.stubs(:value).with(:trace).returns(false) + end + + it "should use :main and :ssl upon initialization" do + Puppet.settings.expects(:use).with(:main, :ssl) + @file_class.new + end + + it "should return a nil collection directory if no directory setting has been provided" do + @file_class.store_in nil + @file_class.collection_directory.should be_nil + end + + it "should return a nil file location if no location has been provided" do + @file_class.store_at nil + @file_class.file_location.should be_nil + end + + it "should fail if no store directory or file location has been set" do + @file_class.store_in nil + @file_class.store_at nil + lambda { @file_class.new }.should raise_error(Puppet::DevError) + end + + describe "when managing ssl files" do + before do + Puppet.settings.stubs(:use) + @searcher = @file_class.new + + @cert = stub 'certificate', :name => "myname" + @certpath = File.join(@path, "myname" + ".pem") + + @request = stub 'request', :key => @cert.name, :instance => @cert + end + + it "should consider the file a ca file if the name is equal to what the SSL::Host class says is the CA name" do + Puppet::SSL::Host.expects(:ca_name).returns "amaca" + @searcher.should be_ca("amaca") + end + + describe "when choosing the location for certificates" do + it "should set them at the ca setting's path if a ca setting is available and the name resolves to the CA name" do + @file_class.store_in nil + @file_class.store_at :mysetting + @file_class.store_ca_at :casetting + + Puppet.settings.stubs(:value).with(:casetting).returns "/ca/file" + + @searcher.expects(:ca?).with(@cert.name).returns true + @searcher.path(@cert.name).should == "/ca/file" + end + + it "should set them at the file location if a file setting is available" do + @file_class.store_in nil + @file_class.store_at :mysetting + + Puppet.settings.stubs(:value).with(:mysetting).returns "/some/file" + + @searcher.path(@cert.name).should == "/some/file" + end + + it "should set them in the setting directory, with the certificate name plus '.pem', if a directory setting is available" do + @searcher.path(@cert.name).should == @certpath + end + end + + describe "when finding certificates on disk" do + describe "and no certificate is present" do + before do + # Stub things so the case management bits work. + FileTest.stubs(:exist?).with(File.dirname(@certpath)).returns false + FileTest.expects(:exist?).with(@certpath).returns false + end + + it "should return nil" do + @searcher.find(@request).should be_nil + end + end + + describe "and a certificate is present" do + before do + FileTest.expects(:exist?).with(@certpath).returns true + end + + it "should return an instance of the model, which it should use to read the certificate" do + cert = mock 'cert' + model = mock 'model' + @file_class.stubs(:model).returns model + + model.expects(:new).with("myname").returns cert + cert.expects(:read).with(@certpath) + @searcher.find(@request).should equal(cert) + end + end + + describe "and a certificate is present but has uppercase letters" do + before do + @request = stub 'request', :key => "myhost" + end + + # This is kind of more an integration test; it's for #1382, until + # the support for upper-case certs can be removed around mid-2009. + it "should rename the existing file to the lower-case path" do + @path = @searcher.path("myhost") + FileTest.expects(:exist?).with(@path).returns(false) + dir, file = File.split(@path) + FileTest.expects(:exist?).with(dir).returns true + Dir.expects(:entries).with(dir).returns [".", "..", "something.pem", file.upcase] + + File.expects(:rename).with(File.join(dir, file.upcase), @path) + + cert = mock 'cert' + model = mock 'model' + @searcher.stubs(:model).returns model + @searcher.model.expects(:new).with("myhost").returns cert + cert.expects(:read).with(@path) + + @searcher.find(@request) + end + end + end + + describe "when saving certificates to disk" do + before do + FileTest.stubs(:directory?).returns true + FileTest.stubs(:writable?).returns true + end + + it "should fail if the directory is absent" do + FileTest.expects(:directory?).with(File.dirname(@certpath)).returns false + lambda { @searcher.save(@request) }.should raise_error(Puppet::Error) + end + + it "should fail if the directory is not writeable" do + FileTest.stubs(:directory?).returns true + FileTest.expects(:writable?).with(File.dirname(@certpath)).returns false + lambda { @searcher.save(@request) }.should raise_error(Puppet::Error) + end + + it "should save to the path the output of converting the certificate to a string" do + fh = mock 'filehandle' + fh.expects(:print).with("mycert") + + @searcher.stubs(:write).yields fh + @cert.expects(:to_s).returns "mycert" + + @searcher.save(@request) + end + + describe "and a directory setting is set" do + it "should use the Settings class to write the file" do + @searcher.class.store_in @setting + fh = mock 'filehandle' + fh.stubs :print + Puppet.settings.expects(:writesub).with(@setting, @certpath).yields fh + + @searcher.save(@request) + end + end + + describe "and a file location is set" do + it "should use the filehandle provided by the Settings" do + @searcher.class.store_at @setting + + fh = mock 'filehandle' + fh.stubs :print + Puppet.settings.expects(:write).with(@setting).yields fh + @searcher.save(@request) + end + end + + describe "and the name is the CA name and a ca setting is set" do + it "should use the filehandle provided by the Settings" do + @searcher.class.store_at @setting + @searcher.class.store_ca_at :castuff + Puppet.settings.stubs(:value).with(:castuff).returns "castuff stub" + + fh = mock 'filehandle' + fh.stubs :print + Puppet.settings.expects(:write).with(:castuff).yields fh + @searcher.stubs(:ca?).returns true + @searcher.save(@request) + end + end + end + + describe "when destroying certificates" do + describe "that do not exist" do + before do + FileTest.expects(:exist?).with(@certpath).returns false + end + + it "should return false" do + @searcher.destroy(@request).should be_false + end + end + + describe "that exist" do + before do + FileTest.expects(:exist?).with(@certpath).returns true + end + + it "should unlink the certificate file" do + File.expects(:unlink).with(@certpath) + @searcher.destroy(@request) + end + + it "should log that is removing the file" do + File.stubs(:exist?).returns true + File.stubs(:unlink) + Puppet.expects(:notice) + @searcher.destroy(@request) + end + end + end + + describe "when searching for certificates" do + before do + @model = mock 'model' + @file_class.stubs(:model).returns @model + end + it "should return a certificate instance for all files that exist" do + Dir.expects(:entries).with(@path).returns %w{one.pem two.pem} + + one = stub 'one', :read => nil + two = stub 'two', :read => nil + + @model.expects(:new).with("one").returns one + @model.expects(:new).with("two").returns two + + @searcher.search(@request).should == [one, two] + end + + it "should read each certificate in using the model's :read method" do + Dir.expects(:entries).with(@path).returns %w{one.pem} + + one = stub 'one' + one.expects(:read).with(File.join(@path, "one.pem")) + + @model.expects(:new).with("one").returns one + + @searcher.search(@request) + end + + it "should skip any files that do not match /\.pem$/" do + Dir.expects(:entries).with(@path).returns %w{. .. one.pem} + + one = stub 'one', :read => nil + + @model.expects(:new).with("one").returns one + + @searcher.search(@request) + end + end + end +end diff --git a/spec/unit/indirector/ssl_file_spec_spec.rb b/spec/unit/indirector/ssl_file_spec_spec.rb deleted file mode 100755 index 8f46409fe..000000000 --- a/spec/unit/indirector/ssl_file_spec_spec.rb +++ /dev/null @@ -1,281 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-10. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/indirector/ssl_file' - -describe Puppet::Indirector::SslFile do - before do - @model = mock 'model' - @indirection = stub 'indirection', :name => :testing, :model => @model - Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection) - @file_class = Class.new(Puppet::Indirector::SslFile) do - def self.to_s - "Testing::Mytype" - end - end - - @setting = :mydir - @file_class.store_in @setting - @path = "/my/directory" - Puppet.settings.stubs(:value).with(:noop).returns(false) - Puppet.settings.stubs(:value).with(@setting).returns(@path) - Puppet.settings.stubs(:value).with(:trace).returns(false) - end - - it "should use :main and :ssl upon initialization" do - Puppet.settings.expects(:use).with(:main, :ssl) - @file_class.new - end - - it "should return a nil collection directory if no directory setting has been provided" do - @file_class.store_in nil - @file_class.collection_directory.should be_nil - end - - it "should return a nil file location if no location has been provided" do - @file_class.store_at nil - @file_class.file_location.should be_nil - end - - it "should fail if no store directory or file location has been set" do - @file_class.store_in nil - @file_class.store_at nil - lambda { @file_class.new }.should raise_error(Puppet::DevError) - end - - describe "when managing ssl files" do - before do - Puppet.settings.stubs(:use) - @searcher = @file_class.new - - @cert = stub 'certificate', :name => "myname" - @certpath = File.join(@path, "myname" + ".pem") - - @request = stub 'request', :key => @cert.name, :instance => @cert - end - - it "should consider the file a ca file if the name is equal to what the SSL::Host class says is the CA name" do - Puppet::SSL::Host.expects(:ca_name).returns "amaca" - @searcher.should be_ca("amaca") - end - - describe "when choosing the location for certificates" do - it "should set them at the ca setting's path if a ca setting is available and the name resolves to the CA name" do - @file_class.store_in nil - @file_class.store_at :mysetting - @file_class.store_ca_at :casetting - - Puppet.settings.stubs(:value).with(:casetting).returns "/ca/file" - - @searcher.expects(:ca?).with(@cert.name).returns true - @searcher.path(@cert.name).should == "/ca/file" - end - - it "should set them at the file location if a file setting is available" do - @file_class.store_in nil - @file_class.store_at :mysetting - - Puppet.settings.stubs(:value).with(:mysetting).returns "/some/file" - - @searcher.path(@cert.name).should == "/some/file" - end - - it "should set them in the setting directory, with the certificate name plus '.pem', if a directory setting is available" do - @searcher.path(@cert.name).should == @certpath - end - end - - describe "when finding certificates on disk" do - describe "and no certificate is present" do - before do - # Stub things so the case management bits work. - FileTest.stubs(:exist?).with(File.dirname(@certpath)).returns false - FileTest.expects(:exist?).with(@certpath).returns false - end - - it "should return nil" do - @searcher.find(@request).should be_nil - end - end - - describe "and a certificate is present" do - before do - FileTest.expects(:exist?).with(@certpath).returns true - end - - it "should return an instance of the model, which it should use to read the certificate" do - cert = mock 'cert' - model = mock 'model' - @file_class.stubs(:model).returns model - - model.expects(:new).with("myname").returns cert - cert.expects(:read).with(@certpath) - @searcher.find(@request).should equal(cert) - end - end - - describe "and a certificate is present but has uppercase letters" do - before do - @request = stub 'request', :key => "myhost" - end - - # This is kind of more an integration test; it's for #1382, until - # the support for upper-case certs can be removed around mid-2009. - it "should rename the existing file to the lower-case path" do - @path = @searcher.path("myhost") - FileTest.expects(:exist?).with(@path).returns(false) - dir, file = File.split(@path) - FileTest.expects(:exist?).with(dir).returns true - Dir.expects(:entries).with(dir).returns [".", "..", "something.pem", file.upcase] - - File.expects(:rename).with(File.join(dir, file.upcase), @path) - - cert = mock 'cert' - model = mock 'model' - @searcher.stubs(:model).returns model - @searcher.model.expects(:new).with("myhost").returns cert - cert.expects(:read).with(@path) - - @searcher.find(@request) - end - end - end - - describe "when saving certificates to disk" do - before do - FileTest.stubs(:directory?).returns true - FileTest.stubs(:writable?).returns true - end - - it "should fail if the directory is absent" do - FileTest.expects(:directory?).with(File.dirname(@certpath)).returns false - lambda { @searcher.save(@request) }.should raise_error(Puppet::Error) - end - - it "should fail if the directory is not writeable" do - FileTest.stubs(:directory?).returns true - FileTest.expects(:writable?).with(File.dirname(@certpath)).returns false - lambda { @searcher.save(@request) }.should raise_error(Puppet::Error) - end - - it "should save to the path the output of converting the certificate to a string" do - fh = mock 'filehandle' - fh.expects(:print).with("mycert") - - @searcher.stubs(:write).yields fh - @cert.expects(:to_s).returns "mycert" - - @searcher.save(@request) - end - - describe "and a directory setting is set" do - it "should use the Settings class to write the file" do - @searcher.class.store_in @setting - fh = mock 'filehandle' - fh.stubs :print - Puppet.settings.expects(:writesub).with(@setting, @certpath).yields fh - - @searcher.save(@request) - end - end - - describe "and a file location is set" do - it "should use the filehandle provided by the Settings" do - @searcher.class.store_at @setting - - fh = mock 'filehandle' - fh.stubs :print - Puppet.settings.expects(:write).with(@setting).yields fh - @searcher.save(@request) - end - end - - describe "and the name is the CA name and a ca setting is set" do - it "should use the filehandle provided by the Settings" do - @searcher.class.store_at @setting - @searcher.class.store_ca_at :castuff - Puppet.settings.stubs(:value).with(:castuff).returns "castuff stub" - - fh = mock 'filehandle' - fh.stubs :print - Puppet.settings.expects(:write).with(:castuff).yields fh - @searcher.stubs(:ca?).returns true - @searcher.save(@request) - end - end - end - - describe "when destroying certificates" do - describe "that do not exist" do - before do - FileTest.expects(:exist?).with(@certpath).returns false - end - - it "should return false" do - @searcher.destroy(@request).should be_false - end - end - - describe "that exist" do - before do - FileTest.expects(:exist?).with(@certpath).returns true - end - - it "should unlink the certificate file" do - File.expects(:unlink).with(@certpath) - @searcher.destroy(@request) - end - - it "should log that is removing the file" do - File.stubs(:exist?).returns true - File.stubs(:unlink) - Puppet.expects(:notice) - @searcher.destroy(@request) - end - end - end - - describe "when searching for certificates" do - before do - @model = mock 'model' - @file_class.stubs(:model).returns @model - end - it "should return a certificate instance for all files that exist" do - Dir.expects(:entries).with(@path).returns %w{one.pem two.pem} - - one = stub 'one', :read => nil - two = stub 'two', :read => nil - - @model.expects(:new).with("one").returns one - @model.expects(:new).with("two").returns two - - @searcher.search(@request).should == [one, two] - end - - it "should read each certificate in using the model's :read method" do - Dir.expects(:entries).with(@path).returns %w{one.pem} - - one = stub 'one' - one.expects(:read).with(File.join(@path, "one.pem")) - - @model.expects(:new).with("one").returns one - - @searcher.search(@request) - end - - it "should skip any files that do not match /\.pem$/" do - Dir.expects(:entries).with(@path).returns %w{. .. one.pem} - - one = stub 'one', :read => nil - - @model.expects(:new).with("one").returns one - - @searcher.search(@request) - end - end - end -end diff --git a/spec/unit/indirector/status/rest_spec.rb b/spec/unit/indirector/status/rest_spec.rb new file mode 100755 index 000000000..8f803a253 --- /dev/null +++ b/spec/unit/indirector/status/rest_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/indirector/status/rest' + +describe Puppet::Indirector::Status::Rest do + it "should be a sublcass of Puppet::Indirector::REST" do + Puppet::Indirector::Status::Rest.superclass.should equal(Puppet::Indirector::REST) + end +end diff --git a/spec/unit/indirector/status/rest_spec_spec.rb b/spec/unit/indirector/status/rest_spec_spec.rb deleted file mode 100755 index 8f803a253..000000000 --- a/spec/unit/indirector/status/rest_spec_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/indirector/status/rest' - -describe Puppet::Indirector::Status::Rest do - it "should be a sublcass of Puppet::Indirector::REST" do - Puppet::Indirector::Status::Rest.superclass.should equal(Puppet::Indirector::REST) - end -end diff --git a/spec/unit/indirector/terminus_spec.rb b/spec/unit/indirector/terminus_spec.rb new file mode 100755 index 000000000..3fcbf9d0c --- /dev/null +++ b/spec/unit/indirector/terminus_spec.rb @@ -0,0 +1,245 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/defaults' +require 'puppet/indirector' +require 'puppet/indirector/file' + +describe Puppet::Indirector::Terminus do + before :each do + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @indirection = stub 'indirection', :name => :my_stuff, :register_terminus_type => nil + Puppet::Indirector::Indirection.stubs(:instance).with(:my_stuff).returns(@indirection) + @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do + def self.to_s + "Testing::Abstract" + end + end + @terminus_class = Class.new(@abstract_terminus) do + def self.to_s + "MyStuff::TermType" + end + end + @terminus = @terminus_class.new + end + + describe Puppet::Indirector::Terminus do + + it "should provide a method for setting terminus class documentation" do + @terminus_class.should respond_to(:desc) + end + + it "should support a class-level name attribute" do + @terminus_class.should respond_to(:name) + end + + it "should support a class-level indirection attribute" do + @terminus_class.should respond_to(:indirection) + end + + it "should support a class-level terminus-type attribute" do + @terminus_class.should respond_to(:terminus_type) + end + + it "should support a class-level model attribute" do + @terminus_class.should respond_to(:model) + end + + it "should accept indirection instances as its indirection" do + indirection = stub 'indirection', :is_a? => true, :register_terminus_type => nil + proc { @terminus_class.indirection = indirection }.should_not raise_error + @terminus_class.indirection.should equal(indirection) + end + + it "should look up indirection instances when only a name has been provided" do + indirection = mock 'indirection' + Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(indirection) + @terminus_class.indirection = :myind + @terminus_class.indirection.should equal(indirection) + end + + it "should fail when provided a name that does not resolve to an indirection" do + Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(nil) + proc { @terminus_class.indirection = :myind }.should raise_error(ArgumentError) + + # It shouldn't overwrite our existing one (or, more normally, it shouldn't set + # anything). + @terminus_class.indirection.should equal(@indirection) + end + end + + describe Puppet::Indirector::Terminus, " when creating terminus classes" do + it "should associate the subclass with an indirection based on the subclass constant" do + @terminus.indirection.should equal(@indirection) + end + + it "should set the subclass's type to the abstract terminus name" do + @terminus.terminus_type.should == :abstract + end + + it "should set the subclass's name to the indirection name" do + @terminus.name.should == :term_type + end + + it "should set the subclass's model to the indirection model" do + @indirection.expects(:model).returns :yay + @terminus.model.should == :yay + end + end + + describe Puppet::Indirector::Terminus, " when a terminus instance" do + + it "should return the class's name as its name" do + @terminus.name.should == :term_type + end + + it "should return the class's indirection as its indirection" do + @terminus.indirection.should equal(@indirection) + end + + it "should set the instances's type to the abstract terminus type's name" do + @terminus.terminus_type.should == :abstract + end + + it "should set the instances's model to the indirection's model" do + @indirection.expects(:model).returns :yay + @terminus.model.should == :yay + end + end +end + +# LAK: This could reasonably be in the Indirection instances, too. It doesn't make +# a whole heckuva lot of difference, except that with the instance loading in +# the Terminus base class, we have to have a check to see if we're already +# instance-loading a given terminus class type. +describe Puppet::Indirector::Terminus, " when managing terminus classes" do + it "should provide a method for registering terminus classes" do + Puppet::Indirector::Terminus.should respond_to(:register_terminus_class) + end + + it "should provide a method for returning terminus classes by name and type" do + terminus = stub 'terminus_type', :name => :abstract, :indirection_name => :whatever + Puppet::Indirector::Terminus.register_terminus_class(terminus) + Puppet::Indirector::Terminus.terminus_class(:whatever, :abstract).should equal(terminus) + end + + it "should set up autoloading for any terminus class types requested" do + Puppet::Indirector::Terminus.expects(:instance_load).with(:test2, "puppet/indirector/test2") + Puppet::Indirector::Terminus.terminus_class(:test2, :whatever) + end + + it "should load terminus classes that are not found" do + # Set up instance loading; it would normally happen automatically + Puppet::Indirector::Terminus.instance_load :test1, "puppet/indirector/test1" + + Puppet::Indirector::Terminus.instance_loader(:test1).expects(:load).with(:yay) + Puppet::Indirector::Terminus.terminus_class(:test1, :yay) + end + + it "should fail when no indirection can be found" do + Puppet::Indirector::Indirection.expects(:instance).with(:my_indirection).returns(nil) + + @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do + def self.to_s + "Abstract" + end + end + proc { + @terminus = Class.new(@abstract_terminus) do + def self.to_s + "MyIndirection::TestType" + end + end + }.should raise_error(ArgumentError) + end + + it "should register the terminus class with the terminus base class" do + Puppet::Indirector::Terminus.expects(:register_terminus_class).with do |type| + type.indirection_name == :my_indirection and type.name == :test_terminus + end + @indirection = stub 'indirection', :name => :my_indirection, :register_terminus_type => nil + Puppet::Indirector::Indirection.expects(:instance).with(:my_indirection).returns(@indirection) + + @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do + def self.to_s + "Abstract" + end + end + + @terminus = Class.new(@abstract_terminus) do + def self.to_s + "MyIndirection::TestTerminus" + end + end + end +end + +describe Puppet::Indirector::Terminus, " when parsing class constants for indirection and terminus names" do + before do + @subclass = mock 'subclass' + @subclass.stubs(:to_s).returns("TestInd::OneTwo") + @subclass.stubs(:mark_as_abstract_terminus) + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + end + + it "should fail when anonymous classes are used" do + proc { Puppet::Indirector::Terminus.inherited(Class.new) }.should raise_error(Puppet::DevError) + end + + it "should use the last term in the constant for the terminus class name" do + @subclass.expects(:name=).with(:one_two) + @subclass.stubs(:indirection=) + Puppet::Indirector::Terminus.inherited(@subclass) + end + + it "should convert the terminus name to a downcased symbol" do + @subclass.expects(:name=).with(:one_two) + @subclass.stubs(:indirection=) + Puppet::Indirector::Terminus.inherited(@subclass) + end + + it "should use the second to last term in the constant for the indirection name" do + @subclass.expects(:indirection=).with(:test_ind) + @subclass.stubs(:name=) + @subclass.stubs(:terminus_type=) + Puppet::Indirector::File.inherited(@subclass) + end + + it "should convert the indirection name to a downcased symbol" do + @subclass.expects(:indirection=).with(:test_ind) + @subclass.stubs(:name=) + @subclass.stubs(:terminus_type=) + Puppet::Indirector::File.inherited(@subclass) + end + + it "should convert camel case to lower case with underscores as word separators" do + @subclass.expects(:name=).with(:one_two) + @subclass.stubs(:indirection=) + + Puppet::Indirector::Terminus.inherited(@subclass) + end +end + +describe Puppet::Indirector::Terminus, " when creating terminus class types" do + before do + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @subclass = Class.new(Puppet::Indirector::Terminus) do + def self.to_s + "Puppet::Indirector::Terminus::MyTermType" + end + end + end + + it "should set the name of the abstract subclass to be its class constant" do + @subclass.name.should equal(:my_term_type) + end + + it "should mark abstract terminus types as such" do + @subclass.should be_abstract_terminus + end + + it "should not allow instances of abstract subclasses to be created" do + proc { @subclass.new }.should raise_error(Puppet::DevError) + end +end + diff --git a/spec/unit/indirector/terminus_spec_spec.rb b/spec/unit/indirector/terminus_spec_spec.rb deleted file mode 100755 index 3fcbf9d0c..000000000 --- a/spec/unit/indirector/terminus_spec_spec.rb +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/defaults' -require 'puppet/indirector' -require 'puppet/indirector/file' - -describe Puppet::Indirector::Terminus do - before :each do - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @indirection = stub 'indirection', :name => :my_stuff, :register_terminus_type => nil - Puppet::Indirector::Indirection.stubs(:instance).with(:my_stuff).returns(@indirection) - @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do - def self.to_s - "Testing::Abstract" - end - end - @terminus_class = Class.new(@abstract_terminus) do - def self.to_s - "MyStuff::TermType" - end - end - @terminus = @terminus_class.new - end - - describe Puppet::Indirector::Terminus do - - it "should provide a method for setting terminus class documentation" do - @terminus_class.should respond_to(:desc) - end - - it "should support a class-level name attribute" do - @terminus_class.should respond_to(:name) - end - - it "should support a class-level indirection attribute" do - @terminus_class.should respond_to(:indirection) - end - - it "should support a class-level terminus-type attribute" do - @terminus_class.should respond_to(:terminus_type) - end - - it "should support a class-level model attribute" do - @terminus_class.should respond_to(:model) - end - - it "should accept indirection instances as its indirection" do - indirection = stub 'indirection', :is_a? => true, :register_terminus_type => nil - proc { @terminus_class.indirection = indirection }.should_not raise_error - @terminus_class.indirection.should equal(indirection) - end - - it "should look up indirection instances when only a name has been provided" do - indirection = mock 'indirection' - Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(indirection) - @terminus_class.indirection = :myind - @terminus_class.indirection.should equal(indirection) - end - - it "should fail when provided a name that does not resolve to an indirection" do - Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(nil) - proc { @terminus_class.indirection = :myind }.should raise_error(ArgumentError) - - # It shouldn't overwrite our existing one (or, more normally, it shouldn't set - # anything). - @terminus_class.indirection.should equal(@indirection) - end - end - - describe Puppet::Indirector::Terminus, " when creating terminus classes" do - it "should associate the subclass with an indirection based on the subclass constant" do - @terminus.indirection.should equal(@indirection) - end - - it "should set the subclass's type to the abstract terminus name" do - @terminus.terminus_type.should == :abstract - end - - it "should set the subclass's name to the indirection name" do - @terminus.name.should == :term_type - end - - it "should set the subclass's model to the indirection model" do - @indirection.expects(:model).returns :yay - @terminus.model.should == :yay - end - end - - describe Puppet::Indirector::Terminus, " when a terminus instance" do - - it "should return the class's name as its name" do - @terminus.name.should == :term_type - end - - it "should return the class's indirection as its indirection" do - @terminus.indirection.should equal(@indirection) - end - - it "should set the instances's type to the abstract terminus type's name" do - @terminus.terminus_type.should == :abstract - end - - it "should set the instances's model to the indirection's model" do - @indirection.expects(:model).returns :yay - @terminus.model.should == :yay - end - end -end - -# LAK: This could reasonably be in the Indirection instances, too. It doesn't make -# a whole heckuva lot of difference, except that with the instance loading in -# the Terminus base class, we have to have a check to see if we're already -# instance-loading a given terminus class type. -describe Puppet::Indirector::Terminus, " when managing terminus classes" do - it "should provide a method for registering terminus classes" do - Puppet::Indirector::Terminus.should respond_to(:register_terminus_class) - end - - it "should provide a method for returning terminus classes by name and type" do - terminus = stub 'terminus_type', :name => :abstract, :indirection_name => :whatever - Puppet::Indirector::Terminus.register_terminus_class(terminus) - Puppet::Indirector::Terminus.terminus_class(:whatever, :abstract).should equal(terminus) - end - - it "should set up autoloading for any terminus class types requested" do - Puppet::Indirector::Terminus.expects(:instance_load).with(:test2, "puppet/indirector/test2") - Puppet::Indirector::Terminus.terminus_class(:test2, :whatever) - end - - it "should load terminus classes that are not found" do - # Set up instance loading; it would normally happen automatically - Puppet::Indirector::Terminus.instance_load :test1, "puppet/indirector/test1" - - Puppet::Indirector::Terminus.instance_loader(:test1).expects(:load).with(:yay) - Puppet::Indirector::Terminus.terminus_class(:test1, :yay) - end - - it "should fail when no indirection can be found" do - Puppet::Indirector::Indirection.expects(:instance).with(:my_indirection).returns(nil) - - @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do - def self.to_s - "Abstract" - end - end - proc { - @terminus = Class.new(@abstract_terminus) do - def self.to_s - "MyIndirection::TestType" - end - end - }.should raise_error(ArgumentError) - end - - it "should register the terminus class with the terminus base class" do - Puppet::Indirector::Terminus.expects(:register_terminus_class).with do |type| - type.indirection_name == :my_indirection and type.name == :test_terminus - end - @indirection = stub 'indirection', :name => :my_indirection, :register_terminus_type => nil - Puppet::Indirector::Indirection.expects(:instance).with(:my_indirection).returns(@indirection) - - @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do - def self.to_s - "Abstract" - end - end - - @terminus = Class.new(@abstract_terminus) do - def self.to_s - "MyIndirection::TestTerminus" - end - end - end -end - -describe Puppet::Indirector::Terminus, " when parsing class constants for indirection and terminus names" do - before do - @subclass = mock 'subclass' - @subclass.stubs(:to_s).returns("TestInd::OneTwo") - @subclass.stubs(:mark_as_abstract_terminus) - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - end - - it "should fail when anonymous classes are used" do - proc { Puppet::Indirector::Terminus.inherited(Class.new) }.should raise_error(Puppet::DevError) - end - - it "should use the last term in the constant for the terminus class name" do - @subclass.expects(:name=).with(:one_two) - @subclass.stubs(:indirection=) - Puppet::Indirector::Terminus.inherited(@subclass) - end - - it "should convert the terminus name to a downcased symbol" do - @subclass.expects(:name=).with(:one_two) - @subclass.stubs(:indirection=) - Puppet::Indirector::Terminus.inherited(@subclass) - end - - it "should use the second to last term in the constant for the indirection name" do - @subclass.expects(:indirection=).with(:test_ind) - @subclass.stubs(:name=) - @subclass.stubs(:terminus_type=) - Puppet::Indirector::File.inherited(@subclass) - end - - it "should convert the indirection name to a downcased symbol" do - @subclass.expects(:indirection=).with(:test_ind) - @subclass.stubs(:name=) - @subclass.stubs(:terminus_type=) - Puppet::Indirector::File.inherited(@subclass) - end - - it "should convert camel case to lower case with underscores as word separators" do - @subclass.expects(:name=).with(:one_two) - @subclass.stubs(:indirection=) - - Puppet::Indirector::Terminus.inherited(@subclass) - end -end - -describe Puppet::Indirector::Terminus, " when creating terminus class types" do - before do - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @subclass = Class.new(Puppet::Indirector::Terminus) do - def self.to_s - "Puppet::Indirector::Terminus::MyTermType" - end - end - end - - it "should set the name of the abstract subclass to be its class constant" do - @subclass.name.should equal(:my_term_type) - end - - it "should mark abstract terminus types as such" do - @subclass.should be_abstract_terminus - end - - it "should not allow instances of abstract subclasses to be created" do - proc { @subclass.new }.should raise_error(Puppet::DevError) - end -end - diff --git a/spec/unit/indirector/yaml_spec.rb b/spec/unit/indirector/yaml_spec.rb new file mode 100755 index 000000000..37c33f0cb --- /dev/null +++ b/spec/unit/indirector/yaml_spec.rb @@ -0,0 +1,146 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/indirector/yaml' + +describe Puppet::Indirector::Yaml, " when choosing file location" do + before :each do + @indirection = stub 'indirection', :name => :my_yaml, :register_terminus_type => nil + Puppet::Indirector::Indirection.stubs(:instance).with(:my_yaml).returns(@indirection) + @store_class = Class.new(Puppet::Indirector::Yaml) do + def self.to_s + "MyYaml::MyType" + end + end + @store = @store_class.new + + @subject = Object.new + @subject.singleton_class.send(:attr_accessor, :name) + @subject.name = :me + + @dir = "/what/ever" + Puppet.settings.stubs(:value).returns("fakesettingdata") + Puppet.settings.stubs(:value).with(:clientyamldir).returns(@dir) + Puppet.mode.stubs(:master?).returns false + + @request = stub 'request', :key => :me, :instance => @subject + end + + describe Puppet::Indirector::Yaml, " when choosing file location" do + it "should use the server_datadir if the mode is master" do + Puppet.mode.expects(:master?).returns true + Puppet.settings.expects(:value).with(:yamldir).returns "/server/yaml/dir" + @store.path(:me).should =~ %r{^/server/yaml/dir} + end + + it "should use the client yamldir if the mode is not master" do + Puppet.mode.expects(:master?).returns false + Puppet.settings.expects(:value).with(:clientyamldir).returns "/client/yaml/dir" + @store.path(:me).should =~ %r{^/client/yaml/dir} + end + + it "should store all files in a single file root set in the Puppet defaults" do + @store.path(:me).should =~ %r{^#{@dir}} + end + + it "should use the terminus name for choosing the subdirectory" do + @store.path(:me).should =~ %r{^#{@dir}/my_yaml} + end + + it "should use the object's name to determine the file name" do + @store.path(:me).should =~ %r{me.yaml$} + end + end + + describe Puppet::Indirector::Yaml, " when storing objects as YAML" do + it "should only store objects that respond to :name" do + @request.stubs(:instance).returns Object.new + proc { @store.save(@request) }.should raise_error(ArgumentError) + end + + it "should convert Ruby objects to YAML and write them to disk using a write lock" do + yaml = @subject.to_yaml + file = mock 'file' + path = @store.send(:path, @subject.name) + FileTest.expects(:exist?).with(File.dirname(path)).returns(true) + @store.expects(:writelock).with(path, 0660).yields(file) + file.expects(:print).with(yaml) + + @store.save(@request) + end + + it "should create the indirection subdirectory if it does not exist" do + yaml = @subject.to_yaml + file = mock 'file' + path = @store.send(:path, @subject.name) + dir = File.dirname(path) + + FileTest.expects(:exist?).with(dir).returns(false) + Dir.expects(:mkdir).with(dir) + + @store.expects(:writelock).yields(file) + file.expects(:print).with(yaml) + + @store.save(@request) + end + end + + describe Puppet::Indirector::Yaml, " when retrieving YAML" do + it "should read YAML in from disk using a read lock and convert it to Ruby objects" do + path = @store.send(:path, @subject.name) + + yaml = @subject.to_yaml + FileTest.expects(:exist?).with(path).returns(true) + + fh = mock 'filehandle' + @store.expects(:readlock).with(path).yields fh + fh.expects(:read).returns yaml + + @store.find(@request).instance_variable_get("@name").should == :me + end + + it "should fail coherently when the stored YAML is invalid" do + path = @store.send(:path, @subject.name) + FileTest.expects(:exist?).with(path).returns(true) + + # Something that will fail in yaml + yaml = "--- !ruby/object:Hash" + + fh = mock 'filehandle' + @store.expects(:readlock).yields fh + fh.expects(:read).returns yaml + + proc { @store.find(@request) }.should raise_error(Puppet::Error) + end + end + + describe Puppet::Indirector::Yaml, " when searching" do + it "should return an array of fact instances with one instance for each file when globbing *" do + @request = stub 'request', :key => "*", :instance => @subject + @one = mock 'one' + @two = mock 'two' + @store.expects(:base).returns "/my/yaml/dir" + Dir.expects(:glob).with(File.join("/my/yaml/dir", @store.class.indirection_name.to_s, @request.key)).returns(%w{one.yaml two.yaml}) + YAML.expects(:load_file).with("one.yaml").returns @one; + YAML.expects(:load_file).with("two.yaml").returns @two; + @store.search(@request).should == [@one, @two] + end + + it "should return an array containing a single instance of fact when globbing 'one*'" do + @request = stub 'request', :key => "one*", :instance => @subject + @one = mock 'one' + @store.expects(:base).returns "/my/yaml/dir" + Dir.expects(:glob).with(File.join("/my/yaml/dir", @store.class.indirection_name.to_s, @request.key)).returns(%w{one.yaml}) + YAML.expects(:load_file).with("one.yaml").returns @one; + @store.search(@request).should == [@one] + end + + it "should return an empty array when the glob doesn't match anything" do + @request = stub 'request', :key => "f*ilglobcanfail*", :instance => @subject + @store.expects(:base).returns "/my/yaml/dir" + Dir.expects(:glob).with(File.join("/my/yaml/dir", @store.class.indirection_name.to_s, @request.key)).returns([]) + @store.search(@request).should == [] + end + end +end diff --git a/spec/unit/indirector/yaml_spec_spec.rb b/spec/unit/indirector/yaml_spec_spec.rb deleted file mode 100755 index 37c33f0cb..000000000 --- a/spec/unit/indirector/yaml_spec_spec.rb +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/indirector/yaml' - -describe Puppet::Indirector::Yaml, " when choosing file location" do - before :each do - @indirection = stub 'indirection', :name => :my_yaml, :register_terminus_type => nil - Puppet::Indirector::Indirection.stubs(:instance).with(:my_yaml).returns(@indirection) - @store_class = Class.new(Puppet::Indirector::Yaml) do - def self.to_s - "MyYaml::MyType" - end - end - @store = @store_class.new - - @subject = Object.new - @subject.singleton_class.send(:attr_accessor, :name) - @subject.name = :me - - @dir = "/what/ever" - Puppet.settings.stubs(:value).returns("fakesettingdata") - Puppet.settings.stubs(:value).with(:clientyamldir).returns(@dir) - Puppet.mode.stubs(:master?).returns false - - @request = stub 'request', :key => :me, :instance => @subject - end - - describe Puppet::Indirector::Yaml, " when choosing file location" do - it "should use the server_datadir if the mode is master" do - Puppet.mode.expects(:master?).returns true - Puppet.settings.expects(:value).with(:yamldir).returns "/server/yaml/dir" - @store.path(:me).should =~ %r{^/server/yaml/dir} - end - - it "should use the client yamldir if the mode is not master" do - Puppet.mode.expects(:master?).returns false - Puppet.settings.expects(:value).with(:clientyamldir).returns "/client/yaml/dir" - @store.path(:me).should =~ %r{^/client/yaml/dir} - end - - it "should store all files in a single file root set in the Puppet defaults" do - @store.path(:me).should =~ %r{^#{@dir}} - end - - it "should use the terminus name for choosing the subdirectory" do - @store.path(:me).should =~ %r{^#{@dir}/my_yaml} - end - - it "should use the object's name to determine the file name" do - @store.path(:me).should =~ %r{me.yaml$} - end - end - - describe Puppet::Indirector::Yaml, " when storing objects as YAML" do - it "should only store objects that respond to :name" do - @request.stubs(:instance).returns Object.new - proc { @store.save(@request) }.should raise_error(ArgumentError) - end - - it "should convert Ruby objects to YAML and write them to disk using a write lock" do - yaml = @subject.to_yaml - file = mock 'file' - path = @store.send(:path, @subject.name) - FileTest.expects(:exist?).with(File.dirname(path)).returns(true) - @store.expects(:writelock).with(path, 0660).yields(file) - file.expects(:print).with(yaml) - - @store.save(@request) - end - - it "should create the indirection subdirectory if it does not exist" do - yaml = @subject.to_yaml - file = mock 'file' - path = @store.send(:path, @subject.name) - dir = File.dirname(path) - - FileTest.expects(:exist?).with(dir).returns(false) - Dir.expects(:mkdir).with(dir) - - @store.expects(:writelock).yields(file) - file.expects(:print).with(yaml) - - @store.save(@request) - end - end - - describe Puppet::Indirector::Yaml, " when retrieving YAML" do - it "should read YAML in from disk using a read lock and convert it to Ruby objects" do - path = @store.send(:path, @subject.name) - - yaml = @subject.to_yaml - FileTest.expects(:exist?).with(path).returns(true) - - fh = mock 'filehandle' - @store.expects(:readlock).with(path).yields fh - fh.expects(:read).returns yaml - - @store.find(@request).instance_variable_get("@name").should == :me - end - - it "should fail coherently when the stored YAML is invalid" do - path = @store.send(:path, @subject.name) - FileTest.expects(:exist?).with(path).returns(true) - - # Something that will fail in yaml - yaml = "--- !ruby/object:Hash" - - fh = mock 'filehandle' - @store.expects(:readlock).yields fh - fh.expects(:read).returns yaml - - proc { @store.find(@request) }.should raise_error(Puppet::Error) - end - end - - describe Puppet::Indirector::Yaml, " when searching" do - it "should return an array of fact instances with one instance for each file when globbing *" do - @request = stub 'request', :key => "*", :instance => @subject - @one = mock 'one' - @two = mock 'two' - @store.expects(:base).returns "/my/yaml/dir" - Dir.expects(:glob).with(File.join("/my/yaml/dir", @store.class.indirection_name.to_s, @request.key)).returns(%w{one.yaml two.yaml}) - YAML.expects(:load_file).with("one.yaml").returns @one; - YAML.expects(:load_file).with("two.yaml").returns @two; - @store.search(@request).should == [@one, @two] - end - - it "should return an array containing a single instance of fact when globbing 'one*'" do - @request = stub 'request', :key => "one*", :instance => @subject - @one = mock 'one' - @store.expects(:base).returns "/my/yaml/dir" - Dir.expects(:glob).with(File.join("/my/yaml/dir", @store.class.indirection_name.to_s, @request.key)).returns(%w{one.yaml}) - YAML.expects(:load_file).with("one.yaml").returns @one; - @store.search(@request).should == [@one] - end - - it "should return an empty array when the glob doesn't match anything" do - @request = stub 'request', :key => "f*ilglobcanfail*", :instance => @subject - @store.expects(:base).returns "/my/yaml/dir" - Dir.expects(:glob).with(File.join("/my/yaml/dir", @store.class.indirection_name.to_s, @request.key)).returns([]) - @store.search(@request).should == [] - end - end -end diff --git a/spec/unit/indirector_spec.rb b/spec/unit/indirector_spec.rb new file mode 100755 index 000000000..806bcecbc --- /dev/null +++ b/spec/unit/indirector_spec.rb @@ -0,0 +1,157 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'puppet/defaults' +require 'puppet/indirector' + +describe Puppet::Indirector, " when available to a model" do + before do + @thingie = Class.new do + extend Puppet::Indirector + end + end + + it "should provide a way for the model to register an indirection under a name" do + @thingie.should respond_to(:indirects) + end +end + +describe Puppet::Indirector, "when registering an indirection" do + before do + @thingie = Class.new do + extend Puppet::Indirector + attr_reader :name + def initialize(name) + @name = name + end + end + end + + it "should require a name when registering a model" do + Proc.new {@thingie.send(:indirects) }.should raise_error(ArgumentError) + end + + it "should create an indirection instance to manage each indirecting model" do + @indirection = @thingie.indirects(:test) + @indirection.should be_instance_of(Puppet::Indirector::Indirection) + end + + it "should not allow a model to register under multiple names" do + # Keep track of the indirection instance so we can delete it on cleanup + @indirection = @thingie.indirects :first + Proc.new { @thingie.indirects :second }.should raise_error(ArgumentError) + end + + it "should make the indirection available via an accessor" do + @indirection = @thingie.indirects :first + @thingie.indirection.should equal(@indirection) + end + + it "should pass any provided options to the indirection during initialization" do + klass = mock 'terminus class' + Puppet::Indirector::Indirection.expects(:new).with(@thingie, :first, {:some => :options}) + @indirection = @thingie.indirects :first, :some => :options + end + + it "should extend the class with the Format Handler" do + @indirection = @thingie.indirects :first + @thingie.singleton_class.ancestors.should be_include(Puppet::Network::FormatHandler) + end + + after do + @indirection.delete if @indirection + end +end + +describe "Delegated Indirection Method", :shared => true do + it "should delegate to the indirection" do + @indirection.expects(@method) + @thingie.send(@method, "me") + end + + it "should pass all of the passed arguments directly to the indirection instance" do + @indirection.expects(@method).with("me", :one => :two) + @thingie.send(@method, "me", :one => :two) + end + + it "should return the results of the delegation as its result" do + request = mock 'request' + @indirection.expects(@method).returns "yay" + @thingie.send(@method, "me").should == "yay" + end +end + +describe Puppet::Indirector, "when redirecting a model" do + before do + @thingie = Class.new do + extend Puppet::Indirector + attr_reader :name + def initialize(name) + @name = name + end + end + @indirection = @thingie.send(:indirects, :test) + end + + it "should include the Envelope module in the model" do + @thingie.ancestors.should be_include(Puppet::Indirector::Envelope) + end + + describe "when finding instances via the model" do + before { @method = :find } + it_should_behave_like "Delegated Indirection Method" + end + + describe "when destroying instances via the model" do + before { @method = :destroy } + it_should_behave_like "Delegated Indirection Method" + end + + describe "when searching for instances via the model" do + before { @method = :search } + it_should_behave_like "Delegated Indirection Method" + end + + describe "when expiring instances via the model" do + before { @method = :expire } + it_should_behave_like "Delegated Indirection Method" + end + + # This is an instance method, so it behaves a bit differently. + describe "when saving instances via the model" do + before do + @instance = @thingie.new("me") + end + + it "should delegate to the indirection" do + @indirection.expects(:save) + @instance.save + end + + it "should pass the instance and an optional key to the indirection's :save method" do + @indirection.expects(:save).with("key", @instance) + @instance.save "key" + end + + it "should return the results of the delegation as its result" do + request = mock 'request' + @indirection.expects(:save).returns "yay" + @instance.save.should == "yay" + end + end + + it "should give the model the ability to set the indirection terminus class" do + @indirection.expects(:terminus_class=).with(:myterm) + @thingie.terminus_class = :myterm + end + + it "should give the model the ability to set the indirection cache class" do + @indirection.expects(:cache_class=).with(:mycache) + @thingie.cache_class = :mycache + end + + after do + @indirection.delete + end +end diff --git a/spec/unit/indirector_spec_spec.rb b/spec/unit/indirector_spec_spec.rb deleted file mode 100755 index 806bcecbc..000000000 --- a/spec/unit/indirector_spec_spec.rb +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -require 'puppet/defaults' -require 'puppet/indirector' - -describe Puppet::Indirector, " when available to a model" do - before do - @thingie = Class.new do - extend Puppet::Indirector - end - end - - it "should provide a way for the model to register an indirection under a name" do - @thingie.should respond_to(:indirects) - end -end - -describe Puppet::Indirector, "when registering an indirection" do - before do - @thingie = Class.new do - extend Puppet::Indirector - attr_reader :name - def initialize(name) - @name = name - end - end - end - - it "should require a name when registering a model" do - Proc.new {@thingie.send(:indirects) }.should raise_error(ArgumentError) - end - - it "should create an indirection instance to manage each indirecting model" do - @indirection = @thingie.indirects(:test) - @indirection.should be_instance_of(Puppet::Indirector::Indirection) - end - - it "should not allow a model to register under multiple names" do - # Keep track of the indirection instance so we can delete it on cleanup - @indirection = @thingie.indirects :first - Proc.new { @thingie.indirects :second }.should raise_error(ArgumentError) - end - - it "should make the indirection available via an accessor" do - @indirection = @thingie.indirects :first - @thingie.indirection.should equal(@indirection) - end - - it "should pass any provided options to the indirection during initialization" do - klass = mock 'terminus class' - Puppet::Indirector::Indirection.expects(:new).with(@thingie, :first, {:some => :options}) - @indirection = @thingie.indirects :first, :some => :options - end - - it "should extend the class with the Format Handler" do - @indirection = @thingie.indirects :first - @thingie.singleton_class.ancestors.should be_include(Puppet::Network::FormatHandler) - end - - after do - @indirection.delete if @indirection - end -end - -describe "Delegated Indirection Method", :shared => true do - it "should delegate to the indirection" do - @indirection.expects(@method) - @thingie.send(@method, "me") - end - - it "should pass all of the passed arguments directly to the indirection instance" do - @indirection.expects(@method).with("me", :one => :two) - @thingie.send(@method, "me", :one => :two) - end - - it "should return the results of the delegation as its result" do - request = mock 'request' - @indirection.expects(@method).returns "yay" - @thingie.send(@method, "me").should == "yay" - end -end - -describe Puppet::Indirector, "when redirecting a model" do - before do - @thingie = Class.new do - extend Puppet::Indirector - attr_reader :name - def initialize(name) - @name = name - end - end - @indirection = @thingie.send(:indirects, :test) - end - - it "should include the Envelope module in the model" do - @thingie.ancestors.should be_include(Puppet::Indirector::Envelope) - end - - describe "when finding instances via the model" do - before { @method = :find } - it_should_behave_like "Delegated Indirection Method" - end - - describe "when destroying instances via the model" do - before { @method = :destroy } - it_should_behave_like "Delegated Indirection Method" - end - - describe "when searching for instances via the model" do - before { @method = :search } - it_should_behave_like "Delegated Indirection Method" - end - - describe "when expiring instances via the model" do - before { @method = :expire } - it_should_behave_like "Delegated Indirection Method" - end - - # This is an instance method, so it behaves a bit differently. - describe "when saving instances via the model" do - before do - @instance = @thingie.new("me") - end - - it "should delegate to the indirection" do - @indirection.expects(:save) - @instance.save - end - - it "should pass the instance and an optional key to the indirection's :save method" do - @indirection.expects(:save).with("key", @instance) - @instance.save "key" - end - - it "should return the results of the delegation as its result" do - request = mock 'request' - @indirection.expects(:save).returns "yay" - @instance.save.should == "yay" - end - end - - it "should give the model the ability to set the indirection terminus class" do - @indirection.expects(:terminus_class=).with(:myterm) - @thingie.terminus_class = :myterm - end - - it "should give the model the ability to set the indirection cache class" do - @indirection.expects(:cache_class=).with(:mycache) - @thingie.cache_class = :mycache - end - - after do - @indirection.delete - end -end diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb new file mode 100755 index 000000000..85cc84116 --- /dev/null +++ b/spec/unit/module_spec.rb @@ -0,0 +1,546 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +describe Puppet::Module do + before do + # This is necessary because of the extra checks we have for the deprecated + # 'plugins' directory + FileTest.stubs(:exist?).returns false + end + + it "should have a class method that returns a named module from a given environment" do + env = mock 'module' + env.expects(:module).with("mymod").returns "yep" + Puppet::Node::Environment.expects(:new).with("myenv").returns env + + Puppet::Module.find("mymod", "myenv").should == "yep" + end + + it "should return nil if asked for a named module that doesn't exist" do + env = mock 'module' + env.expects(:module).with("mymod").returns nil + Puppet::Node::Environment.expects(:new).with("myenv").returns env + + Puppet::Module.find("mymod", "myenv").should be_nil + end + + it "should support a 'version' attribute" do + mod = Puppet::Module.new("mymod") + mod.version = 1.09 + mod.version.should == 1.09 + end + + it "should support a 'source' attribute" do + mod = Puppet::Module.new("mymod") + mod.source = "http://foo/bar" + mod.source.should == "http://foo/bar" + end + + it "should support a 'project_page' attribute" do + mod = Puppet::Module.new("mymod") + mod.project_page = "http://foo/bar" + mod.project_page.should == "http://foo/bar" + end + + it "should support an 'author' attribute" do + mod = Puppet::Module.new("mymod") + mod.author = "Luke Kanies " + mod.author.should == "Luke Kanies " + end + + it "should support a 'license' attribute" do + mod = Puppet::Module.new("mymod") + mod.license = "GPL2" + mod.license.should == "GPL2" + end + + it "should support a 'summary' attribute" do + mod = Puppet::Module.new("mymod") + mod.summary = "GPL2" + mod.summary.should == "GPL2" + end + + it "should support a 'description' attribute" do + mod = Puppet::Module.new("mymod") + mod.description = "GPL2" + mod.description.should == "GPL2" + end + + it "should support specifying a compatible puppet version" do + mod = Puppet::Module.new("mymod") + mod.puppetversion = "0.25" + mod.puppetversion.should == "0.25" + end + + it "should validate that the puppet version is compatible" do + mod = Puppet::Module.new("mymod") + mod.puppetversion = "0.25" + Puppet.expects(:version).returns "0.25" + mod.validate_puppet_version + end + + it "should fail if the specified puppet version is not compatible" do + mod = Puppet::Module.new("mymod") + mod.puppetversion = "0.25" + Puppet.stubs(:version).returns "0.24" + lambda { mod.validate_puppet_version }.should raise_error(Puppet::Module::IncompatibleModule) + end + + describe "when specifying required modules" do + it "should support specifying a required module" do + mod = Puppet::Module.new("mymod") + mod.requires "foobar" + end + + it "should support specifying multiple required modules" do + mod = Puppet::Module.new("mymod") + mod.requires "foobar" + mod.requires "baz" + end + + it "should support specifying a required module and version" do + mod = Puppet::Module.new("mymod") + mod.requires "foobar", 1.0 + end + + it "should fail when required modules are missing" do + mod = Puppet::Module.new("mymod") + mod.requires "foobar" + + mod.environment.expects(:module).with("foobar").returns nil + + lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::MissingModule) + end + + it "should fail when required modules are present but of the wrong version" do + mod = Puppet::Module.new("mymod") + mod.requires "foobar", 1.0 + + foobar = Puppet::Module.new("foobar") + foobar.version = 2.0 + + mod.environment.expects(:module).with("foobar").returns foobar + + lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::IncompatibleModule) + end + + it "should have valid dependencies when no dependencies have been specified" do + mod = Puppet::Module.new("mymod") + + lambda { mod.validate_dependencies }.should_not raise_error + end + + it "should fail when some dependencies are present but others aren't" do + mod = Puppet::Module.new("mymod") + mod.requires "foobar" + mod.requires "baz" + + mod.environment.expects(:module).with("foobar").returns Puppet::Module.new("foobar") + mod.environment.expects(:module).with("baz").returns nil + + lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::MissingModule) + end + + it "should have valid dependencies when all dependencies are met" do + mod = Puppet::Module.new("mymod") + mod.requires "foobar", 1.0 + mod.requires "baz" + + foobar = Puppet::Module.new("foobar") + foobar.version = 1.0 + + baz = Puppet::Module.new("baz") + + mod.environment.expects(:module).with("foobar").returns foobar + mod.environment.expects(:module).with("baz").returns baz + + lambda { mod.validate_dependencies }.should_not raise_error + end + + it "should validate its dependendencies on initialization" do + Puppet::Module.any_instance.expects(:validate_dependencies) + Puppet::Module.new("mymod") + end + end + + describe "when managing supported platforms" do + it "should support specifying a supported platform" do + mod = Puppet::Module.new("mymod") + mod.supports "solaris" + end + + it "should support specifying a supported platform and version" do + mod = Puppet::Module.new("mymod") + mod.supports "solaris", 1.0 + end + + it "should fail when not running on a supported platform" do + pending "Not sure how to send client platform to the module" + mod = Puppet::Module.new("mymod") + Facter.expects(:value).with("operatingsystem").returns "Solaris" + + mod.supports "hpux" + + lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::UnsupportedPlatform) + end + + it "should fail when supported platforms are present but of the wrong version" do + pending "Not sure how to send client platform to the module" + mod = Puppet::Module.new("mymod") + Facter.expects(:value).with("operatingsystem").returns "Solaris" + Facter.expects(:value).with("operatingsystemrelease").returns 2.0 + + mod.supports "Solaris", 1.0 + + lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) + end + + it "should be considered supported when no supported platforms have been specified" do + pending "Not sure how to send client platform to the module" + mod = Puppet::Module.new("mymod") + lambda { mod.validate_supported_platform }.should_not raise_error + end + + it "should be considered supported when running on a supported platform" do + pending "Not sure how to send client platform to the module" + mod = Puppet::Module.new("mymod") + Facter.expects(:value).with("operatingsystem").returns "Solaris" + Facter.expects(:value).with("operatingsystemrelease").returns 2.0 + + mod.supports "Solaris", 1.0 + + lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) + end + + it "should be considered supported when running on any of multiple supported platforms" do + pending "Not sure how to send client platform to the module" + end + + it "should validate its platform support on initialization" do + pending "Not sure how to send client platform to the module" + end + end + + it "should return nil if asked for a module whose name is 'nil'" do + Puppet::Module.find(nil, "myenv").should be_nil + end + + it "should provide support for logging" do + Puppet::Module.ancestors.should be_include(Puppet::Util::Logging) + end + + it "should be able to be converted to a string" do + Puppet::Module.new("foo").to_s.should == "Module foo" + end + + it "should add the path to its string form if the module is found" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns "/a" + mod.to_s.should == "Module foo(/a)" + end + + it "should fail if its name is not alphanumeric" do + lambda { Puppet::Module.new(".something") }.should raise_error(Puppet::Module::InvalidName) + end + + it "should require a name at initialization" do + lambda { Puppet::Module.new }.should raise_error(ArgumentError) + end + + it "should convert an environment name into an Environment instance" do + Puppet::Module.new("foo", "prod").environment.should be_instance_of(Puppet::Node::Environment) + end + + it "should accept an environment at initialization" do + Puppet::Module.new("foo", :prod).environment.name.should == :prod + end + + it "should use the default environment if none is provided" do + env = Puppet::Node::Environment.new + Puppet::Module.new("foo").environment.should equal(env) + end + + it "should use any provided Environment instance" do + env = Puppet::Node::Environment.new + Puppet::Module.new("foo", env).environment.should equal(env) + end + + it "should return the path to the first found instance in its environment's module paths as its path" do + mod = Puppet::Module.new("foo") + env = mock 'environment' + mod.stubs(:environment).returns env + + env.expects(:modulepath).returns %w{/a /b /c} + + FileTest.expects(:exist?).with("/a/foo").returns false + FileTest.expects(:exist?).with("/b/foo").returns true + FileTest.expects(:exist?).with("/c/foo").never + + mod.path.should == "/b/foo" + end + + it "should be considered existent if it exists in at least one module path" do + mod = Puppet::Module.new("foo") + mod.expects(:path).returns "/a/foo" + mod.should be_exist + end + + it "should be considered nonexistent if it does not exist in any of the module paths" do + mod = Puppet::Module.new("foo") + mod.expects(:path).returns nil + mod.should_not be_exist + end + + [:plugins, :templates, :files, :manifests].each do |filetype| + dirname = filetype == :plugins ? "lib" : filetype.to_s + it "should be able to return individual #{filetype}" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns "/a/foo" + path = File.join("/a/foo", dirname, "my/file") + FileTest.expects(:exist?).with(path).returns true + mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should == path + end + + it "should consider #{filetype} to be present if their base directory exists" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns "/a/foo" + path = File.join("/a/foo", dirname) + FileTest.expects(:exist?).with(path).returns true + mod.send(filetype.to_s + "?").should be_true + end + + it "should consider #{filetype} to be absent if their base directory does not exist" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns "/a/foo" + path = File.join("/a/foo", dirname) + FileTest.expects(:exist?).with(path).returns false + mod.send(filetype.to_s + "?").should be_false + end + + it "should consider #{filetype} to be absent if the module base directory does not exist" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns nil + mod.send(filetype.to_s + "?").should be_false + end + + it "should return nil if asked to return individual #{filetype} that don't exist" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns "/a/foo" + path = File.join("/a/foo", dirname, "my/file") + FileTest.expects(:exist?).with(path).returns false + mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil + end + + it "should return nil when asked for individual #{filetype} if the module does not exist" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns nil + mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil + end + + it "should return the base directory if asked for a nil path" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns "/a/foo" + base = File.join("/a/foo", dirname) + FileTest.expects(:exist?).with(base).returns true + mod.send(filetype.to_s.sub(/s$/, ''), nil).should == base + end + end + + %w{plugins files}.each do |filetype| + short = filetype.sub(/s$/, '') + dirname = filetype == "plugins" ? "lib" : filetype.to_s + it "should be able to return the #{short} directory" do + Puppet::Module.new("foo").should respond_to(short + "_directory") + end + + it "should return the path to the #{short} directory" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns "/a/foo" + + mod.send(short + "_directory").should == "/a/foo/#{dirname}" + end + end + + it "should throw a warning if plugins are in a 'plugins' directory rather than a 'lib' directory" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns "/a/foo" + FileTest.expects(:exist?).with("/a/foo/plugins").returns true + + mod.expects(:warning) + + mod.plugin_directory.should == "/a/foo/plugins" + end + + it "should default to 'lib' for the plugins directory" do + mod = Puppet::Module.new("foo") + mod.stubs(:path).returns "/a/foo" + mod.plugin_directory.should == "/a/foo/lib" + end +end + +describe Puppet::Module, " when building its search path" do + it "should use the current environment's search path if no environment is specified" do + env = mock 'env' + env.expects(:modulepath).returns "eh" + Puppet::Node::Environment.expects(:new).with(nil).returns env + + Puppet::Module.modulepath.should == "eh" + end + + it "should use the specified environment's search path if an environment is specified" do + env = mock 'env' + env.expects(:modulepath).returns "eh" + Puppet::Node::Environment.expects(:new).with("foo").returns env + + Puppet::Module.modulepath("foo").should == "eh" + end +end + +describe Puppet::Module, "when finding matching manifests" do + before do + @mod = Puppet::Module.new("mymod") + @mod.stubs(:path).returns "/a" + end + + it "should return all manifests matching the glob pattern" do + Dir.expects(:glob).with("/a/manifests/yay/*.pp").returns(%w{foo bar}) + FileTest.stubs(:directory?).returns false + + @mod.match_manifests("yay/*.pp").should == %w{foo bar} + end + + it "should not return directories" do + Dir.expects(:glob).with("/a/manifests/yay/*.pp").returns(%w{foo bar}) + + FileTest.expects(:directory?).with("foo").returns false + FileTest.expects(:directory?).with("bar").returns true + @mod.match_manifests("yay/*.pp").should == %w{foo} + end + + it "should default to the 'init.pp' file if no glob pattern is specified" do + FileTest.stubs(:exist?).returns true + + @mod.match_manifests(nil).should == %w{/a/manifests/init.pp} + end + + it "should return all manifests matching the glob pattern in all existing paths" do + Dir.expects(:glob).with("/a/manifests/yay/*.pp").returns(%w{a b}) + + @mod.match_manifests("yay/*.pp").should == %w{a b} + end + + it "should match the glob pattern plus '.pp' if no results are found" do + Dir.expects(:glob).with("/a/manifests/yay/foo").returns([]) + Dir.expects(:glob).with("/a/manifests/yay/foo.pp").returns(%w{yay}) + + @mod.match_manifests("yay/foo").should == %w{yay} + end + + it "should return an empty array if no manifests matched" do + Dir.expects(:glob).with("/a/manifests/yay/*.pp").returns([]) + + @mod.match_manifests("yay/*.pp").should == [] + end +end + +describe Puppet::Module do + before do + Puppet::Module.any_instance.stubs(:path).returns "/my/mod/path" + @module = Puppet::Module.new("foo") + end + + it "should use 'License' in its current path as its metadata file" do + @module.license_file.should == "/my/mod/path/License" + end + + it "should return nil as its license file when the module has no path" do + Puppet::Module.any_instance.stubs(:path).returns nil + Puppet::Module.new("foo").license_file.should be_nil + end + + it "should cache the license file" do + Puppet::Module.any_instance.expects(:path).once.returns nil + mod = Puppet::Module.new("foo") + mod.license_file.should == mod.license_file + end + + it "should use 'metadata.json' in its current path as its metadata file" do + @module.metadata_file.should == "/my/mod/path/metadata.json" + end + + it "should return nil as its metadata file when the module has no path" do + Puppet::Module.any_instance.stubs(:path).returns nil + Puppet::Module.new("foo").metadata_file.should be_nil + end + + it "should cache the metadata file" do + Puppet::Module.any_instance.expects(:path).once.returns nil + mod = Puppet::Module.new("foo") + mod.metadata_file.should == mod.metadata_file + end + + it "should know if it has a metadata file" do + FileTest.expects(:exist?).with(@module.metadata_file).returns true + + @module.should be_has_metadata + end + + it "should know if it is missing a metadata file" do + FileTest.expects(:exist?).with(@module.metadata_file).returns false + + @module.should_not be_has_metadata + end + + it "should be able to parse its metadata file" do + @module.should respond_to(:load_metadata) + end + + it "should parse its metadata file on initialization if it is present" do + Puppet::Module.any_instance.expects(:has_metadata?).returns true + Puppet::Module.any_instance.expects(:load_metadata) + + Puppet::Module.new("yay") + end + + describe "when loading the medatada file" do + confine "Cannot test module metadata without json" => Puppet.features.json? + + before do + @data = { + :license => "GPL2", + :author => "luke", + :version => "1.0", + :source => "http://foo/", + :puppetversion => "0.25" + } + @text = @data.to_json + + @module = Puppet::Module.new("foo") + @module.stubs(:metadata_file).returns "/my/file" + File.stubs(:read).with("/my/file").returns @text + end + + %w{source author version license}.each do |attr| + it "should set #{attr} if present in the metadata file" do + @module.load_metadata + @module.send(attr).should == @data[attr.to_sym] + end + + it "should fail if #{attr} is not present in the metadata file" do + @data.delete(attr.to_sym) + @text = @data.to_json + File.stubs(:read).with("/my/file").returns @text + lambda { @module.load_metadata }.should raise_error(Puppet::Module::MissingMetadata) + end + end + + it "should set puppetversion if present in the metadata file" do + @module.load_metadata + @module.puppetversion.should == @data[:puppetversion] + end + + + it "should fail if the discovered name is different than the metadata name" + end +end diff --git a/spec/unit/module_spec_spec.rb b/spec/unit/module_spec_spec.rb deleted file mode 100755 index 85cc84116..000000000 --- a/spec/unit/module_spec_spec.rb +++ /dev/null @@ -1,546 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -describe Puppet::Module do - before do - # This is necessary because of the extra checks we have for the deprecated - # 'plugins' directory - FileTest.stubs(:exist?).returns false - end - - it "should have a class method that returns a named module from a given environment" do - env = mock 'module' - env.expects(:module).with("mymod").returns "yep" - Puppet::Node::Environment.expects(:new).with("myenv").returns env - - Puppet::Module.find("mymod", "myenv").should == "yep" - end - - it "should return nil if asked for a named module that doesn't exist" do - env = mock 'module' - env.expects(:module).with("mymod").returns nil - Puppet::Node::Environment.expects(:new).with("myenv").returns env - - Puppet::Module.find("mymod", "myenv").should be_nil - end - - it "should support a 'version' attribute" do - mod = Puppet::Module.new("mymod") - mod.version = 1.09 - mod.version.should == 1.09 - end - - it "should support a 'source' attribute" do - mod = Puppet::Module.new("mymod") - mod.source = "http://foo/bar" - mod.source.should == "http://foo/bar" - end - - it "should support a 'project_page' attribute" do - mod = Puppet::Module.new("mymod") - mod.project_page = "http://foo/bar" - mod.project_page.should == "http://foo/bar" - end - - it "should support an 'author' attribute" do - mod = Puppet::Module.new("mymod") - mod.author = "Luke Kanies " - mod.author.should == "Luke Kanies " - end - - it "should support a 'license' attribute" do - mod = Puppet::Module.new("mymod") - mod.license = "GPL2" - mod.license.should == "GPL2" - end - - it "should support a 'summary' attribute" do - mod = Puppet::Module.new("mymod") - mod.summary = "GPL2" - mod.summary.should == "GPL2" - end - - it "should support a 'description' attribute" do - mod = Puppet::Module.new("mymod") - mod.description = "GPL2" - mod.description.should == "GPL2" - end - - it "should support specifying a compatible puppet version" do - mod = Puppet::Module.new("mymod") - mod.puppetversion = "0.25" - mod.puppetversion.should == "0.25" - end - - it "should validate that the puppet version is compatible" do - mod = Puppet::Module.new("mymod") - mod.puppetversion = "0.25" - Puppet.expects(:version).returns "0.25" - mod.validate_puppet_version - end - - it "should fail if the specified puppet version is not compatible" do - mod = Puppet::Module.new("mymod") - mod.puppetversion = "0.25" - Puppet.stubs(:version).returns "0.24" - lambda { mod.validate_puppet_version }.should raise_error(Puppet::Module::IncompatibleModule) - end - - describe "when specifying required modules" do - it "should support specifying a required module" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar" - end - - it "should support specifying multiple required modules" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar" - mod.requires "baz" - end - - it "should support specifying a required module and version" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar", 1.0 - end - - it "should fail when required modules are missing" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar" - - mod.environment.expects(:module).with("foobar").returns nil - - lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::MissingModule) - end - - it "should fail when required modules are present but of the wrong version" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar", 1.0 - - foobar = Puppet::Module.new("foobar") - foobar.version = 2.0 - - mod.environment.expects(:module).with("foobar").returns foobar - - lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::IncompatibleModule) - end - - it "should have valid dependencies when no dependencies have been specified" do - mod = Puppet::Module.new("mymod") - - lambda { mod.validate_dependencies }.should_not raise_error - end - - it "should fail when some dependencies are present but others aren't" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar" - mod.requires "baz" - - mod.environment.expects(:module).with("foobar").returns Puppet::Module.new("foobar") - mod.environment.expects(:module).with("baz").returns nil - - lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::MissingModule) - end - - it "should have valid dependencies when all dependencies are met" do - mod = Puppet::Module.new("mymod") - mod.requires "foobar", 1.0 - mod.requires "baz" - - foobar = Puppet::Module.new("foobar") - foobar.version = 1.0 - - baz = Puppet::Module.new("baz") - - mod.environment.expects(:module).with("foobar").returns foobar - mod.environment.expects(:module).with("baz").returns baz - - lambda { mod.validate_dependencies }.should_not raise_error - end - - it "should validate its dependendencies on initialization" do - Puppet::Module.any_instance.expects(:validate_dependencies) - Puppet::Module.new("mymod") - end - end - - describe "when managing supported platforms" do - it "should support specifying a supported platform" do - mod = Puppet::Module.new("mymod") - mod.supports "solaris" - end - - it "should support specifying a supported platform and version" do - mod = Puppet::Module.new("mymod") - mod.supports "solaris", 1.0 - end - - it "should fail when not running on a supported platform" do - pending "Not sure how to send client platform to the module" - mod = Puppet::Module.new("mymod") - Facter.expects(:value).with("operatingsystem").returns "Solaris" - - mod.supports "hpux" - - lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::UnsupportedPlatform) - end - - it "should fail when supported platforms are present but of the wrong version" do - pending "Not sure how to send client platform to the module" - mod = Puppet::Module.new("mymod") - Facter.expects(:value).with("operatingsystem").returns "Solaris" - Facter.expects(:value).with("operatingsystemrelease").returns 2.0 - - mod.supports "Solaris", 1.0 - - lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) - end - - it "should be considered supported when no supported platforms have been specified" do - pending "Not sure how to send client platform to the module" - mod = Puppet::Module.new("mymod") - lambda { mod.validate_supported_platform }.should_not raise_error - end - - it "should be considered supported when running on a supported platform" do - pending "Not sure how to send client platform to the module" - mod = Puppet::Module.new("mymod") - Facter.expects(:value).with("operatingsystem").returns "Solaris" - Facter.expects(:value).with("operatingsystemrelease").returns 2.0 - - mod.supports "Solaris", 1.0 - - lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) - end - - it "should be considered supported when running on any of multiple supported platforms" do - pending "Not sure how to send client platform to the module" - end - - it "should validate its platform support on initialization" do - pending "Not sure how to send client platform to the module" - end - end - - it "should return nil if asked for a module whose name is 'nil'" do - Puppet::Module.find(nil, "myenv").should be_nil - end - - it "should provide support for logging" do - Puppet::Module.ancestors.should be_include(Puppet::Util::Logging) - end - - it "should be able to be converted to a string" do - Puppet::Module.new("foo").to_s.should == "Module foo" - end - - it "should add the path to its string form if the module is found" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns "/a" - mod.to_s.should == "Module foo(/a)" - end - - it "should fail if its name is not alphanumeric" do - lambda { Puppet::Module.new(".something") }.should raise_error(Puppet::Module::InvalidName) - end - - it "should require a name at initialization" do - lambda { Puppet::Module.new }.should raise_error(ArgumentError) - end - - it "should convert an environment name into an Environment instance" do - Puppet::Module.new("foo", "prod").environment.should be_instance_of(Puppet::Node::Environment) - end - - it "should accept an environment at initialization" do - Puppet::Module.new("foo", :prod).environment.name.should == :prod - end - - it "should use the default environment if none is provided" do - env = Puppet::Node::Environment.new - Puppet::Module.new("foo").environment.should equal(env) - end - - it "should use any provided Environment instance" do - env = Puppet::Node::Environment.new - Puppet::Module.new("foo", env).environment.should equal(env) - end - - it "should return the path to the first found instance in its environment's module paths as its path" do - mod = Puppet::Module.new("foo") - env = mock 'environment' - mod.stubs(:environment).returns env - - env.expects(:modulepath).returns %w{/a /b /c} - - FileTest.expects(:exist?).with("/a/foo").returns false - FileTest.expects(:exist?).with("/b/foo").returns true - FileTest.expects(:exist?).with("/c/foo").never - - mod.path.should == "/b/foo" - end - - it "should be considered existent if it exists in at least one module path" do - mod = Puppet::Module.new("foo") - mod.expects(:path).returns "/a/foo" - mod.should be_exist - end - - it "should be considered nonexistent if it does not exist in any of the module paths" do - mod = Puppet::Module.new("foo") - mod.expects(:path).returns nil - mod.should_not be_exist - end - - [:plugins, :templates, :files, :manifests].each do |filetype| - dirname = filetype == :plugins ? "lib" : filetype.to_s - it "should be able to return individual #{filetype}" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns "/a/foo" - path = File.join("/a/foo", dirname, "my/file") - FileTest.expects(:exist?).with(path).returns true - mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should == path - end - - it "should consider #{filetype} to be present if their base directory exists" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns "/a/foo" - path = File.join("/a/foo", dirname) - FileTest.expects(:exist?).with(path).returns true - mod.send(filetype.to_s + "?").should be_true - end - - it "should consider #{filetype} to be absent if their base directory does not exist" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns "/a/foo" - path = File.join("/a/foo", dirname) - FileTest.expects(:exist?).with(path).returns false - mod.send(filetype.to_s + "?").should be_false - end - - it "should consider #{filetype} to be absent if the module base directory does not exist" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns nil - mod.send(filetype.to_s + "?").should be_false - end - - it "should return nil if asked to return individual #{filetype} that don't exist" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns "/a/foo" - path = File.join("/a/foo", dirname, "my/file") - FileTest.expects(:exist?).with(path).returns false - mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil - end - - it "should return nil when asked for individual #{filetype} if the module does not exist" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns nil - mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil - end - - it "should return the base directory if asked for a nil path" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns "/a/foo" - base = File.join("/a/foo", dirname) - FileTest.expects(:exist?).with(base).returns true - mod.send(filetype.to_s.sub(/s$/, ''), nil).should == base - end - end - - %w{plugins files}.each do |filetype| - short = filetype.sub(/s$/, '') - dirname = filetype == "plugins" ? "lib" : filetype.to_s - it "should be able to return the #{short} directory" do - Puppet::Module.new("foo").should respond_to(short + "_directory") - end - - it "should return the path to the #{short} directory" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns "/a/foo" - - mod.send(short + "_directory").should == "/a/foo/#{dirname}" - end - end - - it "should throw a warning if plugins are in a 'plugins' directory rather than a 'lib' directory" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns "/a/foo" - FileTest.expects(:exist?).with("/a/foo/plugins").returns true - - mod.expects(:warning) - - mod.plugin_directory.should == "/a/foo/plugins" - end - - it "should default to 'lib' for the plugins directory" do - mod = Puppet::Module.new("foo") - mod.stubs(:path).returns "/a/foo" - mod.plugin_directory.should == "/a/foo/lib" - end -end - -describe Puppet::Module, " when building its search path" do - it "should use the current environment's search path if no environment is specified" do - env = mock 'env' - env.expects(:modulepath).returns "eh" - Puppet::Node::Environment.expects(:new).with(nil).returns env - - Puppet::Module.modulepath.should == "eh" - end - - it "should use the specified environment's search path if an environment is specified" do - env = mock 'env' - env.expects(:modulepath).returns "eh" - Puppet::Node::Environment.expects(:new).with("foo").returns env - - Puppet::Module.modulepath("foo").should == "eh" - end -end - -describe Puppet::Module, "when finding matching manifests" do - before do - @mod = Puppet::Module.new("mymod") - @mod.stubs(:path).returns "/a" - end - - it "should return all manifests matching the glob pattern" do - Dir.expects(:glob).with("/a/manifests/yay/*.pp").returns(%w{foo bar}) - FileTest.stubs(:directory?).returns false - - @mod.match_manifests("yay/*.pp").should == %w{foo bar} - end - - it "should not return directories" do - Dir.expects(:glob).with("/a/manifests/yay/*.pp").returns(%w{foo bar}) - - FileTest.expects(:directory?).with("foo").returns false - FileTest.expects(:directory?).with("bar").returns true - @mod.match_manifests("yay/*.pp").should == %w{foo} - end - - it "should default to the 'init.pp' file if no glob pattern is specified" do - FileTest.stubs(:exist?).returns true - - @mod.match_manifests(nil).should == %w{/a/manifests/init.pp} - end - - it "should return all manifests matching the glob pattern in all existing paths" do - Dir.expects(:glob).with("/a/manifests/yay/*.pp").returns(%w{a b}) - - @mod.match_manifests("yay/*.pp").should == %w{a b} - end - - it "should match the glob pattern plus '.pp' if no results are found" do - Dir.expects(:glob).with("/a/manifests/yay/foo").returns([]) - Dir.expects(:glob).with("/a/manifests/yay/foo.pp").returns(%w{yay}) - - @mod.match_manifests("yay/foo").should == %w{yay} - end - - it "should return an empty array if no manifests matched" do - Dir.expects(:glob).with("/a/manifests/yay/*.pp").returns([]) - - @mod.match_manifests("yay/*.pp").should == [] - end -end - -describe Puppet::Module do - before do - Puppet::Module.any_instance.stubs(:path).returns "/my/mod/path" - @module = Puppet::Module.new("foo") - end - - it "should use 'License' in its current path as its metadata file" do - @module.license_file.should == "/my/mod/path/License" - end - - it "should return nil as its license file when the module has no path" do - Puppet::Module.any_instance.stubs(:path).returns nil - Puppet::Module.new("foo").license_file.should be_nil - end - - it "should cache the license file" do - Puppet::Module.any_instance.expects(:path).once.returns nil - mod = Puppet::Module.new("foo") - mod.license_file.should == mod.license_file - end - - it "should use 'metadata.json' in its current path as its metadata file" do - @module.metadata_file.should == "/my/mod/path/metadata.json" - end - - it "should return nil as its metadata file when the module has no path" do - Puppet::Module.any_instance.stubs(:path).returns nil - Puppet::Module.new("foo").metadata_file.should be_nil - end - - it "should cache the metadata file" do - Puppet::Module.any_instance.expects(:path).once.returns nil - mod = Puppet::Module.new("foo") - mod.metadata_file.should == mod.metadata_file - end - - it "should know if it has a metadata file" do - FileTest.expects(:exist?).with(@module.metadata_file).returns true - - @module.should be_has_metadata - end - - it "should know if it is missing a metadata file" do - FileTest.expects(:exist?).with(@module.metadata_file).returns false - - @module.should_not be_has_metadata - end - - it "should be able to parse its metadata file" do - @module.should respond_to(:load_metadata) - end - - it "should parse its metadata file on initialization if it is present" do - Puppet::Module.any_instance.expects(:has_metadata?).returns true - Puppet::Module.any_instance.expects(:load_metadata) - - Puppet::Module.new("yay") - end - - describe "when loading the medatada file" do - confine "Cannot test module metadata without json" => Puppet.features.json? - - before do - @data = { - :license => "GPL2", - :author => "luke", - :version => "1.0", - :source => "http://foo/", - :puppetversion => "0.25" - } - @text = @data.to_json - - @module = Puppet::Module.new("foo") - @module.stubs(:metadata_file).returns "/my/file" - File.stubs(:read).with("/my/file").returns @text - end - - %w{source author version license}.each do |attr| - it "should set #{attr} if present in the metadata file" do - @module.load_metadata - @module.send(attr).should == @data[attr.to_sym] - end - - it "should fail if #{attr} is not present in the metadata file" do - @data.delete(attr.to_sym) - @text = @data.to_json - File.stubs(:read).with("/my/file").returns @text - lambda { @module.load_metadata }.should raise_error(Puppet::Module::MissingMetadata) - end - end - - it "should set puppetversion if present in the metadata file" do - @module.load_metadata - @module.puppetversion.should == @data[:puppetversion] - end - - - it "should fail if the discovered name is different than the metadata name" - end -end diff --git a/spec/unit/network/authconfig_spec.rb b/spec/unit/network/authconfig_spec.rb new file mode 100755 index 000000000..2bf9ee138 --- /dev/null +++ b/spec/unit/network/authconfig_spec.rb @@ -0,0 +1,292 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/authconfig' + +describe Puppet::Network::AuthConfig do + before do + @rights = stubs 'rights' + Puppet::Network::Rights.stubs(:new).returns(@rights) + @rights.stubs(:each).returns([]) + + FileTest.stubs(:exists?).returns(true) + File.stubs(:stat).returns(stub('stat', :ctime => :now)) + Time.stubs(:now).returns :now + + @authconfig = Puppet::Network::AuthConfig.new("dummy", false) + end + + describe "when initializing" do + before :each do + Puppet::Network::AuthConfig.any_instance.stubs(:read) + end + + it "should use the authconfig default pathname if none provided" do + Puppet.expects(:[]).with(:authconfig).returns("dummy") + + Puppet::Network::AuthConfig.new + end + + it "should raise an error if no file is defined finally" do + Puppet.stubs(:[]).with(:authconfig).returns(nil) + + lambda { Puppet::Network::AuthConfig.new }.should raise_error(Puppet::DevError) + end + + it "should read and parse the file if parsenow is true" do + Puppet::Network::AuthConfig.any_instance.expects(:read) + + Puppet::Network::AuthConfig.new("dummy", true) + end + + end + + describe "when checking authorization" do + before :each do + @authconfig.stubs(:read) + @call = stub 'call', :intern => "name" + @handler = stub 'handler', :intern => "handler" + @method = stub_everything 'method' + @request = stub 'request', :call => @call, :handler => @handler, :method => @method, :name => "me", :ip => "1.2.3.4" + end + + it "should attempt to read the authconfig file" do + @rights.stubs(:include?) + + @authconfig.expects(:read) + + @authconfig.allowed?(@request) + end + + it "should use a name right if it exists" do + right = stub 'right' + + @rights.stubs(:include?).with("name").returns(true) + @rights.stubs(:[]).with("name").returns(right) + + right.expects(:allowed?).with("me", "1.2.3.4") + + @authconfig.allowed?(@request) + end + + it "should use a namespace right otherwise" do + right = stub 'right' + + @rights.stubs(:include?).with("name").returns(false) + @rights.stubs(:include?).with("handler").returns(true) + @rights.stubs(:[]).with("handler").returns(right) + + right.expects(:allowed?).with("me", "1.2.3.4") + + @authconfig.allowed?(@request) + end + + it "should return whatever the found rights returns" do + right = stub 'right' + + @rights.stubs(:include?).with("name").returns(true) + @rights.stubs(:[]).with("name").returns(right) + + right.stubs(:allowed?).with("me", "1.2.3.4").returns(:returned) + + @authconfig.allowed?(@request).should == :returned + end + + end + + describe "when parsing authconfig file" do + before :each do + @fd = stub 'fd' + File.stubs(:open).yields(@fd) + @rights.stubs(:include?).returns(false) + @rights.stubs(:[]) + end + + it "should skip comments" do + @fd.stubs(:each).yields(' # comment') + + @rights.expects(:newright).never + + @authconfig.read + end + + it "should increment line number even on commented lines" do + @fd.stubs(:each).multiple_yields(' # comment','[puppetca]') + + @rights.expects(:newright).with('[puppetca]', 2, 'dummy') + + @authconfig.read + end + + it "should skip blank lines" do + @fd.stubs(:each).yields(' ') + + @rights.expects(:newright).never + + @authconfig.read + end + + it "should increment line number even on blank lines" do + @fd.stubs(:each).multiple_yields(' ','[puppetca]') + + @rights.expects(:newright).with('[puppetca]', 2, 'dummy') + + @authconfig.read + end + + it "should throw an error if the current namespace right already exist" do + @fd.stubs(:each).yields('[puppetca]') + + @rights.stubs(:include?).with("puppetca").returns(true) + + lambda { @authconfig.read }.should raise_error + end + + it "should not throw an error if the current path right already exist" do + @fd.stubs(:each).yields('path /hello') + + @rights.stubs(:newright).with("/hello",1, 'dummy') + @rights.stubs(:include?).with("/hello").returns(true) + + lambda { @authconfig.read }.should_not raise_error + end + + it "should create a new right for found namespaces" do + @fd.stubs(:each).yields('[puppetca]') + + @rights.expects(:newright).with("[puppetca]", 1, 'dummy') + + @authconfig.read + end + + it "should create a new right for each found namespace line" do + @fd.stubs(:each).multiple_yields('[puppetca]', '[fileserver]') + + @rights.expects(:newright).with("[puppetca]", 1, 'dummy') + @rights.expects(:newright).with("[fileserver]", 2, 'dummy') + + @authconfig.read + end + + it "should create a new right for each found path line" do + @fd.stubs(:each).multiple_yields('path /certificates') + + @rights.expects(:newright).with("/certificates", 1, 'dummy') + + @authconfig.read + end + + it "should create a new right for each found regex line" do + @fd.stubs(:each).multiple_yields('path ~ .rb$') + + @rights.expects(:newright).with("~ .rb$", 1, 'dummy') + + @authconfig.read + end + + it "should create an allow ACE on each subsequent allow" do + acl = stub 'acl', :info + + @fd.stubs(:each).multiple_yields('[puppetca]', 'allow 127.0.0.1') + @rights.stubs(:newright).with("[puppetca]", 1, 'dummy').returns(acl) + + acl.expects(:allow).with('127.0.0.1') + + @authconfig.read + end + + it "should create a deny ACE on each subsequent deny" do + acl = stub 'acl', :info + + @fd.stubs(:each).multiple_yields('[puppetca]', 'deny 127.0.0.1') + @rights.stubs(:newright).with("[puppetca]", 1, 'dummy').returns(acl) + + acl.expects(:deny).with('127.0.0.1') + + @authconfig.read + end + + it "should inform the current ACL if we get the 'method' directive" do + acl = stub 'acl', :info + acl.stubs(:acl_type).returns(:regex) + + @fd.stubs(:each).multiple_yields('path /certificates', 'method search,find') + @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) + + acl.expects(:restrict_method).with('search') + acl.expects(:restrict_method).with('find') + + @authconfig.read + end + + it "should raise an error if the 'method' directive is used in a right different than a path/regex one" do + acl = stub 'acl', :info + acl.stubs(:acl_type).returns(:regex) + + @fd.stubs(:each).multiple_yields('[puppetca]', 'method search,find') + @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) + + lambda { @authconfig.read }.should raise_error + end + + it "should inform the current ACL if we get the 'environment' directive" do + acl = stub 'acl', :info + acl.stubs(:acl_type).returns(:regex) + + @fd.stubs(:each).multiple_yields('path /certificates', 'environment production,development') + @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) + + acl.expects(:restrict_environment).with('production') + acl.expects(:restrict_environment).with('development') + + @authconfig.read + end + + it "should raise an error if the 'environment' directive is used in a right different than a path/regex one" do + acl = stub 'acl', :info + acl.stubs(:acl_type).returns(:regex) + + @fd.stubs(:each).multiple_yields('[puppetca]', 'environment env') + @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) + + lambda { @authconfig.read }.should raise_error + end + + it "should inform the current ACL if we get the 'auth' directive" do + acl = stub 'acl', :info + acl.stubs(:acl_type).returns(:regex) + + @fd.stubs(:each).multiple_yields('path /certificates', 'auth yes') + @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) + + acl.expects(:restrict_authenticated).with('yes') + + @authconfig.read + end + + it "should also allow the longest 'authenticated' directive" do + acl = stub 'acl', :info + acl.stubs(:acl_type).returns(:regex) + + @fd.stubs(:each).multiple_yields('path /certificates', 'authenticated yes') + @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) + + acl.expects(:restrict_authenticated).with('yes') + + @authconfig.read + end + + it "should raise an error if the 'auth' directive is used in a right different than a path/regex one" do + acl = stub 'acl', :info + acl.stubs(:acl_type).returns(:regex) + + @fd.stubs(:each).multiple_yields('[puppetca]', 'auth yes') + @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) + + lambda { @authconfig.read }.should raise_error + end + + end + +end diff --git a/spec/unit/network/authconfig_spec_spec.rb b/spec/unit/network/authconfig_spec_spec.rb deleted file mode 100755 index 2bf9ee138..000000000 --- a/spec/unit/network/authconfig_spec_spec.rb +++ /dev/null @@ -1,292 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/authconfig' - -describe Puppet::Network::AuthConfig do - before do - @rights = stubs 'rights' - Puppet::Network::Rights.stubs(:new).returns(@rights) - @rights.stubs(:each).returns([]) - - FileTest.stubs(:exists?).returns(true) - File.stubs(:stat).returns(stub('stat', :ctime => :now)) - Time.stubs(:now).returns :now - - @authconfig = Puppet::Network::AuthConfig.new("dummy", false) - end - - describe "when initializing" do - before :each do - Puppet::Network::AuthConfig.any_instance.stubs(:read) - end - - it "should use the authconfig default pathname if none provided" do - Puppet.expects(:[]).with(:authconfig).returns("dummy") - - Puppet::Network::AuthConfig.new - end - - it "should raise an error if no file is defined finally" do - Puppet.stubs(:[]).with(:authconfig).returns(nil) - - lambda { Puppet::Network::AuthConfig.new }.should raise_error(Puppet::DevError) - end - - it "should read and parse the file if parsenow is true" do - Puppet::Network::AuthConfig.any_instance.expects(:read) - - Puppet::Network::AuthConfig.new("dummy", true) - end - - end - - describe "when checking authorization" do - before :each do - @authconfig.stubs(:read) - @call = stub 'call', :intern => "name" - @handler = stub 'handler', :intern => "handler" - @method = stub_everything 'method' - @request = stub 'request', :call => @call, :handler => @handler, :method => @method, :name => "me", :ip => "1.2.3.4" - end - - it "should attempt to read the authconfig file" do - @rights.stubs(:include?) - - @authconfig.expects(:read) - - @authconfig.allowed?(@request) - end - - it "should use a name right if it exists" do - right = stub 'right' - - @rights.stubs(:include?).with("name").returns(true) - @rights.stubs(:[]).with("name").returns(right) - - right.expects(:allowed?).with("me", "1.2.3.4") - - @authconfig.allowed?(@request) - end - - it "should use a namespace right otherwise" do - right = stub 'right' - - @rights.stubs(:include?).with("name").returns(false) - @rights.stubs(:include?).with("handler").returns(true) - @rights.stubs(:[]).with("handler").returns(right) - - right.expects(:allowed?).with("me", "1.2.3.4") - - @authconfig.allowed?(@request) - end - - it "should return whatever the found rights returns" do - right = stub 'right' - - @rights.stubs(:include?).with("name").returns(true) - @rights.stubs(:[]).with("name").returns(right) - - right.stubs(:allowed?).with("me", "1.2.3.4").returns(:returned) - - @authconfig.allowed?(@request).should == :returned - end - - end - - describe "when parsing authconfig file" do - before :each do - @fd = stub 'fd' - File.stubs(:open).yields(@fd) - @rights.stubs(:include?).returns(false) - @rights.stubs(:[]) - end - - it "should skip comments" do - @fd.stubs(:each).yields(' # comment') - - @rights.expects(:newright).never - - @authconfig.read - end - - it "should increment line number even on commented lines" do - @fd.stubs(:each).multiple_yields(' # comment','[puppetca]') - - @rights.expects(:newright).with('[puppetca]', 2, 'dummy') - - @authconfig.read - end - - it "should skip blank lines" do - @fd.stubs(:each).yields(' ') - - @rights.expects(:newright).never - - @authconfig.read - end - - it "should increment line number even on blank lines" do - @fd.stubs(:each).multiple_yields(' ','[puppetca]') - - @rights.expects(:newright).with('[puppetca]', 2, 'dummy') - - @authconfig.read - end - - it "should throw an error if the current namespace right already exist" do - @fd.stubs(:each).yields('[puppetca]') - - @rights.stubs(:include?).with("puppetca").returns(true) - - lambda { @authconfig.read }.should raise_error - end - - it "should not throw an error if the current path right already exist" do - @fd.stubs(:each).yields('path /hello') - - @rights.stubs(:newright).with("/hello",1, 'dummy') - @rights.stubs(:include?).with("/hello").returns(true) - - lambda { @authconfig.read }.should_not raise_error - end - - it "should create a new right for found namespaces" do - @fd.stubs(:each).yields('[puppetca]') - - @rights.expects(:newright).with("[puppetca]", 1, 'dummy') - - @authconfig.read - end - - it "should create a new right for each found namespace line" do - @fd.stubs(:each).multiple_yields('[puppetca]', '[fileserver]') - - @rights.expects(:newright).with("[puppetca]", 1, 'dummy') - @rights.expects(:newright).with("[fileserver]", 2, 'dummy') - - @authconfig.read - end - - it "should create a new right for each found path line" do - @fd.stubs(:each).multiple_yields('path /certificates') - - @rights.expects(:newright).with("/certificates", 1, 'dummy') - - @authconfig.read - end - - it "should create a new right for each found regex line" do - @fd.stubs(:each).multiple_yields('path ~ .rb$') - - @rights.expects(:newright).with("~ .rb$", 1, 'dummy') - - @authconfig.read - end - - it "should create an allow ACE on each subsequent allow" do - acl = stub 'acl', :info - - @fd.stubs(:each).multiple_yields('[puppetca]', 'allow 127.0.0.1') - @rights.stubs(:newright).with("[puppetca]", 1, 'dummy').returns(acl) - - acl.expects(:allow).with('127.0.0.1') - - @authconfig.read - end - - it "should create a deny ACE on each subsequent deny" do - acl = stub 'acl', :info - - @fd.stubs(:each).multiple_yields('[puppetca]', 'deny 127.0.0.1') - @rights.stubs(:newright).with("[puppetca]", 1, 'dummy').returns(acl) - - acl.expects(:deny).with('127.0.0.1') - - @authconfig.read - end - - it "should inform the current ACL if we get the 'method' directive" do - acl = stub 'acl', :info - acl.stubs(:acl_type).returns(:regex) - - @fd.stubs(:each).multiple_yields('path /certificates', 'method search,find') - @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) - - acl.expects(:restrict_method).with('search') - acl.expects(:restrict_method).with('find') - - @authconfig.read - end - - it "should raise an error if the 'method' directive is used in a right different than a path/regex one" do - acl = stub 'acl', :info - acl.stubs(:acl_type).returns(:regex) - - @fd.stubs(:each).multiple_yields('[puppetca]', 'method search,find') - @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) - - lambda { @authconfig.read }.should raise_error - end - - it "should inform the current ACL if we get the 'environment' directive" do - acl = stub 'acl', :info - acl.stubs(:acl_type).returns(:regex) - - @fd.stubs(:each).multiple_yields('path /certificates', 'environment production,development') - @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) - - acl.expects(:restrict_environment).with('production') - acl.expects(:restrict_environment).with('development') - - @authconfig.read - end - - it "should raise an error if the 'environment' directive is used in a right different than a path/regex one" do - acl = stub 'acl', :info - acl.stubs(:acl_type).returns(:regex) - - @fd.stubs(:each).multiple_yields('[puppetca]', 'environment env') - @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) - - lambda { @authconfig.read }.should raise_error - end - - it "should inform the current ACL if we get the 'auth' directive" do - acl = stub 'acl', :info - acl.stubs(:acl_type).returns(:regex) - - @fd.stubs(:each).multiple_yields('path /certificates', 'auth yes') - @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) - - acl.expects(:restrict_authenticated).with('yes') - - @authconfig.read - end - - it "should also allow the longest 'authenticated' directive" do - acl = stub 'acl', :info - acl.stubs(:acl_type).returns(:regex) - - @fd.stubs(:each).multiple_yields('path /certificates', 'authenticated yes') - @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) - - acl.expects(:restrict_authenticated).with('yes') - - @authconfig.read - end - - it "should raise an error if the 'auth' directive is used in a right different than a path/regex one" do - acl = stub 'acl', :info - acl.stubs(:acl_type).returns(:regex) - - @fd.stubs(:each).multiple_yields('[puppetca]', 'auth yes') - @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) - - lambda { @authconfig.read }.should raise_error - end - - end - -end diff --git a/spec/unit/network/authstore_spec.rb b/spec/unit/network/authstore_spec.rb new file mode 100644 index 000000000..58eb92693 --- /dev/null +++ b/spec/unit/network/authstore_spec.rb @@ -0,0 +1,370 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/authconfig' + +describe Puppet::Network::AuthStore do + describe "when checking if the acl has some entries" do + before :each do + @authstore = Puppet::Network::AuthStore.new + end + + it "should be empty if no ACE have been entered" do + @authstore.should be_empty + end + + it "should not be empty if it is a global allow" do + @authstore.allow('*') + + @authstore.should_not be_empty + end + + it "should not be empty if at least one allow has been entered" do + @authstore.allow('1.1.1.*') + + @authstore.should_not be_empty + end + + it "should not be empty if at least one deny has been entered" do + @authstore.deny('1.1.1.*') + + @authstore.should_not be_empty + end + end +end + +describe Puppet::Network::AuthStore::Declaration do + + ['100.101.99.98','100.100.100.100','1.2.3.4','11.22.33.44'].each { |ip| + describe "when the pattern is a simple numeric IP such as #{ip}" do + before :each do + @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,ip) + end + it "should match the specified IP" do + @declaration.should be_match('www.testsite.org',ip) + end + it "should not match other IPs" do + @declaration.should_not be_match('www.testsite.org','200.101.99.98') + end + end + + (1..3).each { |n| + describe "when the pattern is a IP mask with #{n} numeric segments and a *" do + before :each do + @ip_pattern = ip.split('.')[0,n].join('.')+'.*' + @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@ip_pattern) + end + it "should match an IP in the range" do + @declaration.should be_match('www.testsite.org',ip) + end + it "should not match other IPs" do + @declaration.should_not be_match('www.testsite.org','200.101.99.98') + end + it "should not match IPs that differ in the last non-wildcard segment" do + other = ip.split('.') + other[n-1].succ! + @declaration.should_not be_match('www.testsite.org',other.join('.')) + end + end + } + } + + describe "when the pattern is a numeric IP with a back reference" do + before :each do + @ip = '100.101.$1' + @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@ip).interpolate('12.34'.match(/(.*)/)) + end + it "should match an IP with the appropriate interpolation" do + @declaration.should be_match('www.testsite.org',@ip.sub(/\$1/,'12.34')) + end + it "should not match other IPs" do + @declaration.should_not be_match('www.testsite.org',@ip.sub(/\$1/,'66.34')) + end + end + + [ + "02001:0000:1234:0000:0000:C1C0:ABCD:0876", + "2001:0000:1234:0000:00001:C1C0:ABCD:0876", + " 2001:0000:1234:0000:0000:C1C0:ABCD:0876  0", + "2001:0000:1234: 0000:0000:C1C0:ABCD:0876", + "3ffe:0b00:0000:0001:0000:0000:000a", + "FF02:0000:0000:0000:0000:0000:0000:0000:0001", + "3ffe:b00::1::a", + "1:2:3::4:5::7:8", + "12345::6:7:8", + "1::5:400.2.3.4", + "1::5:260.2.3.4", + "1::5:256.2.3.4", + "1::5:1.256.3.4", + "1::5:1.2.256.4", + "1::5:1.2.3.256", + "1::5:300.2.3.4", + "1::5:1.300.3.4", + "1::5:1.2.300.4", + "1::5:1.2.3.300", + "1::5:900.2.3.4", + "1::5:1.900.3.4", + "1::5:1.2.900.4", + "1::5:1.2.3.900", + "1::5:300.300.300.300", + "1::5:3000.30.30.30", + "1::400.2.3.4", + "1::260.2.3.4", + "1::256.2.3.4", + "1::1.256.3.4", + "1::1.2.256.4", + "1::1.2.3.256", + "1::300.2.3.4", + "1::1.300.3.4", + "1::1.2.300.4", + "1::1.2.3.300", + "1::900.2.3.4", + "1::1.900.3.4", + "1::1.2.900.4", + "1::1.2.3.900", + "1::300.300.300.300", + "1::3000.30.30.30", + "::400.2.3.4", + "::260.2.3.4", + "::256.2.3.4", + "::1.256.3.4", + "::1.2.256.4", + "::1.2.3.256", + "::300.2.3.4", + "::1.300.3.4", + "::1.2.300.4", + "::1.2.3.300", + "::900.2.3.4", + "::1.900.3.4", + "::1.2.900.4", + "::1.2.3.900", + "::300.300.300.300", + "::3000.30.30.30", + "2001:DB8:0:0:8:800:200C:417A:221", # unicast, full + "FF01::101::2" # multicast, compressed + ].each { |invalid_ip| + describe "when the pattern is an invalid IPv6 address such as #{invalid_ip}" do + it "should raise an exception" do + lambda { Puppet::Network::AuthStore::Declaration.new(:allow,invalid_ip) }.should raise_error + end + end + } + + [ + "1.2.3.4", + "2001:0000:1234:0000:0000:C1C0:ABCD:0876", + "3ffe:0b00:0000:0000:0001:0000:0000:000a", + "FF02:0000:0000:0000:0000:0000:0000:0001", + "0000:0000:0000:0000:0000:0000:0000:0001", + "0000:0000:0000:0000:0000:0000:0000:0000", + "::ffff:192.168.1.26", + "2::10", + "ff02::1", + "fe80::", + "2002::", + "2001:db8::", + "2001:0db8:1234::", + "::ffff:0:0", + "::1", + "::ffff:192.168.1.1", + "1:2:3:4:5:6:7:8", + "1:2:3:4:5:6::8", + "1:2:3:4:5::8", + "1:2:3:4::8", + "1:2:3::8", + "1:2::8", + "1::8", + "1::2:3:4:5:6:7", + "1::2:3:4:5:6", + "1::2:3:4:5", + "1::2:3:4", + "1::2:3", + "1::8", + "::2:3:4:5:6:7:8", + "::2:3:4:5:6:7", + "::2:3:4:5:6", + "::2:3:4:5", + "::2:3:4", + "::2:3", + "::8", + "1:2:3:4:5:6::", + "1:2:3:4:5::", + "1:2:3:4::", + "1:2:3::", + "1:2::", + "1::", + "1:2:3:4:5::7:8", + "1:2:3:4::7:8", + "1:2:3::7:8", + "1:2::7:8", + "1::7:8", + "1:2:3:4:5:6:1.2.3.4", + "1:2:3:4:5::1.2.3.4", + "1:2:3:4::1.2.3.4", + "1:2:3::1.2.3.4", + "1:2::1.2.3.4", + "1::1.2.3.4", + "1:2:3:4::5:1.2.3.4", + "1:2:3::5:1.2.3.4", + "1:2::5:1.2.3.4", + "1::5:1.2.3.4", + "1::5:11.22.33.44", + "fe80::217:f2ff:254.7.237.98", + "fe80::217:f2ff:fe07:ed62", + "2001:DB8:0:0:8:800:200C:417A", # unicast, full + "FF01:0:0:0:0:0:0:101", # multicast, full + "0:0:0:0:0:0:0:1", # loopback, full + "0:0:0:0:0:0:0:0", # unspecified, full + "2001:DB8::8:800:200C:417A", # unicast, compressed + "FF01::101", # multicast, compressed + "::1", # loopback, compressed, non-routable + "::", # unspecified, compressed, non-routable + "0:0:0:0:0:0:13.1.68.3", # IPv4-compatible IPv6 address, full, deprecated + "0:0:0:0:0:FFFF:129.144.52.38", # IPv4-mapped IPv6 address, full + "::13.1.68.3", # IPv4-compatible IPv6 address, compressed, deprecated + "::FFFF:129.144.52.38", # IPv4-mapped IPv6 address, compressed + "2001:0DB8:0000:CD30:0000:0000:0000:0000/60", # full, with prefix + "2001:0DB8::CD30:0:0:0:0/60", # compressed, with prefix + "2001:0DB8:0:CD30::/60", # compressed, with prefix #2 + "::/128", # compressed, unspecified address type, non-routable + "::1/128", # compressed, loopback address type, non-routable + "FF00::/8", # compressed, multicast address type + "FE80::/10", # compressed, link-local unicast, non-routable + "FEC0::/10", # compressed, site-local unicast, deprecated + "127.0.0.1", # standard IPv4, loopback, non-routable + "0.0.0.0", # standard IPv4, unspecified, non-routable + "255.255.255.255", # standard IPv4 + "fe80:0000:0000:0000:0204:61ff:fe9d:f156", + "fe80:0:0:0:204:61ff:fe9d:f156", + "fe80::204:61ff:fe9d:f156", + "fe80:0000:0000:0000:0204:61ff:254.157.241.086", + "fe80:0:0:0:204:61ff:254.157.241.86", + "fe80::204:61ff:254.157.241.86", + "::1", + "fe80::", + "fe80::1" + ].each { |ip| + describe "when the pattern is a valid IP such as #{ip}" do + before :each do + @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,ip) + end + it "should match the specified IP" do + @declaration.should be_match('www.testsite.org',ip) + end + it "should not match other IPs" do + @declaration.should_not be_match('www.testsite.org','200.101.99.98') + end + end unless ip =~ /:.*\./ # Hybrid IPs aren't supported by ruby's ipaddr + } + + { + 'spirit.mars.nasa.gov' => 'a PQDN', + 'ratchet.2ndsiteinc.com' => 'a PQDN with digits', + 'a.c.ru' => 'a PQDN with short segments', + }.each {|pqdn,desc| + describe "when the pattern is #{desc}" do + before :each do + @host = pqdn + @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@host) + end + it "should match the specified PQDN" do + @declaration.should be_match(@host,'200.101.99.98') + end + it "should not match a similar FQDN" do + pending "FQDN consensus" + @declaration.should_not be_match(@host+'.','200.101.99.98') + end + end + } + + ['abc.12seps.edu.phisher.biz','www.google.com','slashdot.org'].each { |host| + (1...(host.split('.').length)).each { |n| + describe "when the pattern is #{"*."+host.split('.')[-n,n].join('.')}" do + before :each do + @pattern = "*."+host.split('.')[-n,n].join('.') + @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@pattern) + end + it "should match #{host}" do + @declaration.should be_match(host,'1.2.3.4') + end + it "should not match www.testsite.gov" do + @declaration.should_not be_match('www.testsite.gov','200.101.99.98') + end + it "should not match hosts that differ in the first non-wildcard segment" do + other = host.split('.') + other[-n].succ! + @declaration.should_not be_match(other.join('.'),'1.2.3.4') + end + end + } + } + + describe "when the pattern is a FQDN" do + before :each do + @host = 'spirit.mars.nasa.gov.' + @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@host) + end + it "should match the specified FQDN" do + pending "FQDN consensus" + @declaration.should be_match(@host,'200.101.99.98') + end + it "should not match a similar PQDN" do + @declaration.should_not be_match(@host[0..-2],'200.101.99.98') + end + end + + + describe "when the pattern is an opaque string with a back reference" do + before :each do + @host = 'c216f41a-f902-4bfb-a222-850dd957bebb' + @item = "/catalog/#{@host}" + @pattern = %{^/catalog/([^/]+)$} + @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') + end + it "should match an IP with the appropriate interpolation" do + @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') + end + end + + describe "when the pattern is an opaque string with a back reference and the matched data contains dots" do + before :each do + @host = 'admin.mgmt.nym1' + @item = "/catalog/#{@host}" + @pattern = %{^/catalog/([^/]+)$} + @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') + end + it "should match a name with the appropriate interpolation" do + @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') + end + end + + describe "when the pattern is an opaque string with a back reference and the matched data contains dots with an initial prefix that looks like an IP address" do + before :each do + @host = '01.admin.mgmt.nym1' + @item = "/catalog/#{@host}" + @pattern = %{^/catalog/([^/]+)$} + @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') + end + it "should match a name with the appropriate interpolation" do + @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') + end + end + + describe "when comparing patterns" do + before :each do + @ip = Puppet::Network::AuthStore::Declaration.new(:allow,'127.0.0.1') + @host_name = Puppet::Network::AuthStore::Declaration.new(:allow,'www.hard_knocks.edu') + @opaque = Puppet::Network::AuthStore::Declaration.new(:allow,'hey_dude') + end + it "should consider ip addresses before host names" do + (@ip < @host_name).should be_true + end + it "should consider ip addresses before opaque strings" do + (@ip < @opaque).should be_true + end + it "should consider host_names before opaque strings" do + (@host_name < @opaque).should be_true + end + end +end diff --git a/spec/unit/network/authstore_spec_spec.rb b/spec/unit/network/authstore_spec_spec.rb deleted file mode 100644 index 58eb92693..000000000 --- a/spec/unit/network/authstore_spec_spec.rb +++ /dev/null @@ -1,370 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/authconfig' - -describe Puppet::Network::AuthStore do - describe "when checking if the acl has some entries" do - before :each do - @authstore = Puppet::Network::AuthStore.new - end - - it "should be empty if no ACE have been entered" do - @authstore.should be_empty - end - - it "should not be empty if it is a global allow" do - @authstore.allow('*') - - @authstore.should_not be_empty - end - - it "should not be empty if at least one allow has been entered" do - @authstore.allow('1.1.1.*') - - @authstore.should_not be_empty - end - - it "should not be empty if at least one deny has been entered" do - @authstore.deny('1.1.1.*') - - @authstore.should_not be_empty - end - end -end - -describe Puppet::Network::AuthStore::Declaration do - - ['100.101.99.98','100.100.100.100','1.2.3.4','11.22.33.44'].each { |ip| - describe "when the pattern is a simple numeric IP such as #{ip}" do - before :each do - @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,ip) - end - it "should match the specified IP" do - @declaration.should be_match('www.testsite.org',ip) - end - it "should not match other IPs" do - @declaration.should_not be_match('www.testsite.org','200.101.99.98') - end - end - - (1..3).each { |n| - describe "when the pattern is a IP mask with #{n} numeric segments and a *" do - before :each do - @ip_pattern = ip.split('.')[0,n].join('.')+'.*' - @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@ip_pattern) - end - it "should match an IP in the range" do - @declaration.should be_match('www.testsite.org',ip) - end - it "should not match other IPs" do - @declaration.should_not be_match('www.testsite.org','200.101.99.98') - end - it "should not match IPs that differ in the last non-wildcard segment" do - other = ip.split('.') - other[n-1].succ! - @declaration.should_not be_match('www.testsite.org',other.join('.')) - end - end - } - } - - describe "when the pattern is a numeric IP with a back reference" do - before :each do - @ip = '100.101.$1' - @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@ip).interpolate('12.34'.match(/(.*)/)) - end - it "should match an IP with the appropriate interpolation" do - @declaration.should be_match('www.testsite.org',@ip.sub(/\$1/,'12.34')) - end - it "should not match other IPs" do - @declaration.should_not be_match('www.testsite.org',@ip.sub(/\$1/,'66.34')) - end - end - - [ - "02001:0000:1234:0000:0000:C1C0:ABCD:0876", - "2001:0000:1234:0000:00001:C1C0:ABCD:0876", - " 2001:0000:1234:0000:0000:C1C0:ABCD:0876  0", - "2001:0000:1234: 0000:0000:C1C0:ABCD:0876", - "3ffe:0b00:0000:0001:0000:0000:000a", - "FF02:0000:0000:0000:0000:0000:0000:0000:0001", - "3ffe:b00::1::a", - "1:2:3::4:5::7:8", - "12345::6:7:8", - "1::5:400.2.3.4", - "1::5:260.2.3.4", - "1::5:256.2.3.4", - "1::5:1.256.3.4", - "1::5:1.2.256.4", - "1::5:1.2.3.256", - "1::5:300.2.3.4", - "1::5:1.300.3.4", - "1::5:1.2.300.4", - "1::5:1.2.3.300", - "1::5:900.2.3.4", - "1::5:1.900.3.4", - "1::5:1.2.900.4", - "1::5:1.2.3.900", - "1::5:300.300.300.300", - "1::5:3000.30.30.30", - "1::400.2.3.4", - "1::260.2.3.4", - "1::256.2.3.4", - "1::1.256.3.4", - "1::1.2.256.4", - "1::1.2.3.256", - "1::300.2.3.4", - "1::1.300.3.4", - "1::1.2.300.4", - "1::1.2.3.300", - "1::900.2.3.4", - "1::1.900.3.4", - "1::1.2.900.4", - "1::1.2.3.900", - "1::300.300.300.300", - "1::3000.30.30.30", - "::400.2.3.4", - "::260.2.3.4", - "::256.2.3.4", - "::1.256.3.4", - "::1.2.256.4", - "::1.2.3.256", - "::300.2.3.4", - "::1.300.3.4", - "::1.2.300.4", - "::1.2.3.300", - "::900.2.3.4", - "::1.900.3.4", - "::1.2.900.4", - "::1.2.3.900", - "::300.300.300.300", - "::3000.30.30.30", - "2001:DB8:0:0:8:800:200C:417A:221", # unicast, full - "FF01::101::2" # multicast, compressed - ].each { |invalid_ip| - describe "when the pattern is an invalid IPv6 address such as #{invalid_ip}" do - it "should raise an exception" do - lambda { Puppet::Network::AuthStore::Declaration.new(:allow,invalid_ip) }.should raise_error - end - end - } - - [ - "1.2.3.4", - "2001:0000:1234:0000:0000:C1C0:ABCD:0876", - "3ffe:0b00:0000:0000:0001:0000:0000:000a", - "FF02:0000:0000:0000:0000:0000:0000:0001", - "0000:0000:0000:0000:0000:0000:0000:0001", - "0000:0000:0000:0000:0000:0000:0000:0000", - "::ffff:192.168.1.26", - "2::10", - "ff02::1", - "fe80::", - "2002::", - "2001:db8::", - "2001:0db8:1234::", - "::ffff:0:0", - "::1", - "::ffff:192.168.1.1", - "1:2:3:4:5:6:7:8", - "1:2:3:4:5:6::8", - "1:2:3:4:5::8", - "1:2:3:4::8", - "1:2:3::8", - "1:2::8", - "1::8", - "1::2:3:4:5:6:7", - "1::2:3:4:5:6", - "1::2:3:4:5", - "1::2:3:4", - "1::2:3", - "1::8", - "::2:3:4:5:6:7:8", - "::2:3:4:5:6:7", - "::2:3:4:5:6", - "::2:3:4:5", - "::2:3:4", - "::2:3", - "::8", - "1:2:3:4:5:6::", - "1:2:3:4:5::", - "1:2:3:4::", - "1:2:3::", - "1:2::", - "1::", - "1:2:3:4:5::7:8", - "1:2:3:4::7:8", - "1:2:3::7:8", - "1:2::7:8", - "1::7:8", - "1:2:3:4:5:6:1.2.3.4", - "1:2:3:4:5::1.2.3.4", - "1:2:3:4::1.2.3.4", - "1:2:3::1.2.3.4", - "1:2::1.2.3.4", - "1::1.2.3.4", - "1:2:3:4::5:1.2.3.4", - "1:2:3::5:1.2.3.4", - "1:2::5:1.2.3.4", - "1::5:1.2.3.4", - "1::5:11.22.33.44", - "fe80::217:f2ff:254.7.237.98", - "fe80::217:f2ff:fe07:ed62", - "2001:DB8:0:0:8:800:200C:417A", # unicast, full - "FF01:0:0:0:0:0:0:101", # multicast, full - "0:0:0:0:0:0:0:1", # loopback, full - "0:0:0:0:0:0:0:0", # unspecified, full - "2001:DB8::8:800:200C:417A", # unicast, compressed - "FF01::101", # multicast, compressed - "::1", # loopback, compressed, non-routable - "::", # unspecified, compressed, non-routable - "0:0:0:0:0:0:13.1.68.3", # IPv4-compatible IPv6 address, full, deprecated - "0:0:0:0:0:FFFF:129.144.52.38", # IPv4-mapped IPv6 address, full - "::13.1.68.3", # IPv4-compatible IPv6 address, compressed, deprecated - "::FFFF:129.144.52.38", # IPv4-mapped IPv6 address, compressed - "2001:0DB8:0000:CD30:0000:0000:0000:0000/60", # full, with prefix - "2001:0DB8::CD30:0:0:0:0/60", # compressed, with prefix - "2001:0DB8:0:CD30::/60", # compressed, with prefix #2 - "::/128", # compressed, unspecified address type, non-routable - "::1/128", # compressed, loopback address type, non-routable - "FF00::/8", # compressed, multicast address type - "FE80::/10", # compressed, link-local unicast, non-routable - "FEC0::/10", # compressed, site-local unicast, deprecated - "127.0.0.1", # standard IPv4, loopback, non-routable - "0.0.0.0", # standard IPv4, unspecified, non-routable - "255.255.255.255", # standard IPv4 - "fe80:0000:0000:0000:0204:61ff:fe9d:f156", - "fe80:0:0:0:204:61ff:fe9d:f156", - "fe80::204:61ff:fe9d:f156", - "fe80:0000:0000:0000:0204:61ff:254.157.241.086", - "fe80:0:0:0:204:61ff:254.157.241.86", - "fe80::204:61ff:254.157.241.86", - "::1", - "fe80::", - "fe80::1" - ].each { |ip| - describe "when the pattern is a valid IP such as #{ip}" do - before :each do - @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,ip) - end - it "should match the specified IP" do - @declaration.should be_match('www.testsite.org',ip) - end - it "should not match other IPs" do - @declaration.should_not be_match('www.testsite.org','200.101.99.98') - end - end unless ip =~ /:.*\./ # Hybrid IPs aren't supported by ruby's ipaddr - } - - { - 'spirit.mars.nasa.gov' => 'a PQDN', - 'ratchet.2ndsiteinc.com' => 'a PQDN with digits', - 'a.c.ru' => 'a PQDN with short segments', - }.each {|pqdn,desc| - describe "when the pattern is #{desc}" do - before :each do - @host = pqdn - @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@host) - end - it "should match the specified PQDN" do - @declaration.should be_match(@host,'200.101.99.98') - end - it "should not match a similar FQDN" do - pending "FQDN consensus" - @declaration.should_not be_match(@host+'.','200.101.99.98') - end - end - } - - ['abc.12seps.edu.phisher.biz','www.google.com','slashdot.org'].each { |host| - (1...(host.split('.').length)).each { |n| - describe "when the pattern is #{"*."+host.split('.')[-n,n].join('.')}" do - before :each do - @pattern = "*."+host.split('.')[-n,n].join('.') - @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@pattern) - end - it "should match #{host}" do - @declaration.should be_match(host,'1.2.3.4') - end - it "should not match www.testsite.gov" do - @declaration.should_not be_match('www.testsite.gov','200.101.99.98') - end - it "should not match hosts that differ in the first non-wildcard segment" do - other = host.split('.') - other[-n].succ! - @declaration.should_not be_match(other.join('.'),'1.2.3.4') - end - end - } - } - - describe "when the pattern is a FQDN" do - before :each do - @host = 'spirit.mars.nasa.gov.' - @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@host) - end - it "should match the specified FQDN" do - pending "FQDN consensus" - @declaration.should be_match(@host,'200.101.99.98') - end - it "should not match a similar PQDN" do - @declaration.should_not be_match(@host[0..-2],'200.101.99.98') - end - end - - - describe "when the pattern is an opaque string with a back reference" do - before :each do - @host = 'c216f41a-f902-4bfb-a222-850dd957bebb' - @item = "/catalog/#{@host}" - @pattern = %{^/catalog/([^/]+)$} - @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') - end - it "should match an IP with the appropriate interpolation" do - @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') - end - end - - describe "when the pattern is an opaque string with a back reference and the matched data contains dots" do - before :each do - @host = 'admin.mgmt.nym1' - @item = "/catalog/#{@host}" - @pattern = %{^/catalog/([^/]+)$} - @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') - end - it "should match a name with the appropriate interpolation" do - @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') - end - end - - describe "when the pattern is an opaque string with a back reference and the matched data contains dots with an initial prefix that looks like an IP address" do - before :each do - @host = '01.admin.mgmt.nym1' - @item = "/catalog/#{@host}" - @pattern = %{^/catalog/([^/]+)$} - @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') - end - it "should match a name with the appropriate interpolation" do - @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') - end - end - - describe "when comparing patterns" do - before :each do - @ip = Puppet::Network::AuthStore::Declaration.new(:allow,'127.0.0.1') - @host_name = Puppet::Network::AuthStore::Declaration.new(:allow,'www.hard_knocks.edu') - @opaque = Puppet::Network::AuthStore::Declaration.new(:allow,'hey_dude') - end - it "should consider ip addresses before host names" do - (@ip < @host_name).should be_true - end - it "should consider ip addresses before opaque strings" do - (@ip < @opaque).should be_true - end - it "should consider host_names before opaque strings" do - (@host_name < @opaque).should be_true - end - end -end diff --git a/spec/unit/network/client_spec.rb b/spec/unit/network/client_spec.rb new file mode 100755 index 000000000..cea71d1e5 --- /dev/null +++ b/spec/unit/network/client_spec.rb @@ -0,0 +1,45 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-24. +# Copyright (c) 2008. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/client' + +describe Puppet::Network::Client do + before do + Puppet.settings.stubs(:use).returns(true) + Puppet::Network::HttpPool.stubs(:cert_setup) + end + + describe "when keep-alive is enabled" do + before do + Puppet::Network::HttpPool.stubs(:keep_alive?).returns true + end + it "should start the http client up on creation" do + http = mock 'http' + http.stub_everything + http.expects(:start) + Net::HTTP.stubs(:new).returns http + + # Pick a random subclass... + Puppet::Network::Client.runner.new :Server => Puppet[:server] + end + end + + describe "when keep-alive is disabled" do + before do + Puppet::Network::HttpPool.stubs(:keep_alive?).returns false + end + it "should not start the http client up on creation" do + http = mock 'http' + http.stub_everything + http.expects(:start).never + Net::HTTP.stubs(:new).returns http + + # Pick a random subclass... + Puppet::Network::Client.runner.new :Server => Puppet[:server] + end + end +end diff --git a/spec/unit/network/client_spec_spec.rb b/spec/unit/network/client_spec_spec.rb deleted file mode 100755 index cea71d1e5..000000000 --- a/spec/unit/network/client_spec_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-24. -# Copyright (c) 2008. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/client' - -describe Puppet::Network::Client do - before do - Puppet.settings.stubs(:use).returns(true) - Puppet::Network::HttpPool.stubs(:cert_setup) - end - - describe "when keep-alive is enabled" do - before do - Puppet::Network::HttpPool.stubs(:keep_alive?).returns true - end - it "should start the http client up on creation" do - http = mock 'http' - http.stub_everything - http.expects(:start) - Net::HTTP.stubs(:new).returns http - - # Pick a random subclass... - Puppet::Network::Client.runner.new :Server => Puppet[:server] - end - end - - describe "when keep-alive is disabled" do - before do - Puppet::Network::HttpPool.stubs(:keep_alive?).returns false - end - it "should not start the http client up on creation" do - http = mock 'http' - http.stub_everything - http.expects(:start).never - Net::HTTP.stubs(:new).returns http - - # Pick a random subclass... - Puppet::Network::Client.runner.new :Server => Puppet[:server] - end - end -end diff --git a/spec/unit/network/format_handler_spec.rb b/spec/unit/network/format_handler_spec.rb new file mode 100755 index 000000000..622f7e5a1 --- /dev/null +++ b/spec/unit/network/format_handler_spec.rb @@ -0,0 +1,336 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/format_handler' + +class FormatTester + extend Puppet::Network::FormatHandler +end + +describe Puppet::Network::FormatHandler do + after do + formats = Puppet::Network::FormatHandler.instance_variable_get("@formats") + formats.each do |name, format| + formats.delete(name) unless format.is_a?(Puppet::Network::Format) + end + end + + it "should be able to list supported formats" do + FormatTester.should respond_to(:supported_formats) + end + + it "should include all supported formats" do + one = stub 'supported', :supported? => true, :name => :one, :weight => 1 + two = stub 'supported', :supported? => false, :name => :two, :weight => 1 + three = stub 'supported', :supported? => true, :name => :three, :weight => 1 + four = stub 'supported', :supported? => false, :name => :four, :weight => 1 + Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two, :three, :four] + Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one + Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two + Puppet::Network::FormatHandler.stubs(:format).with(:three).returns three + Puppet::Network::FormatHandler.stubs(:format).with(:four).returns four + result = FormatTester.supported_formats + result.length.should == 2 + result.should be_include(:one) + result.should be_include(:three) + end + + it "should return the supported formats in decreasing order of weight" do + one = stub 'supported', :supported? => true, :name => :one, :weight => 1 + two = stub 'supported', :supported? => true, :name => :two, :weight => 6 + three = stub 'supported', :supported? => true, :name => :three, :weight => 2 + four = stub 'supported', :supported? => true, :name => :four, :weight => 8 + Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two, :three, :four] + Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one + Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two + Puppet::Network::FormatHandler.stubs(:format).with(:three).returns three + Puppet::Network::FormatHandler.stubs(:format).with(:four).returns four + FormatTester.supported_formats.should == [:four, :two, :three, :one] + end + + + describe "with a preferred serialization format setting" do + before do + one = stub 'supported', :supported? => true, :name => :one, :weight => 1 + two = stub 'supported', :supported? => true, :name => :two, :weight => 6 + Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two] + Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one + Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two + end + describe "that is supported" do + before do + Puppet.settings.expects(:value).with(:preferred_serialization_format).returns :one + end + it "should return the preferred serialization format first" do + FormatTester.supported_formats.should == [:one, :two] + end + end + describe "that is not supported" do + before do + Puppet.settings.expects(:value).with(:preferred_serialization_format).returns :unsupported + end + it "should still return the default format first" do + FormatTester.supported_formats.should == [:two, :one] + end + it "should log a debug message" do + Puppet.expects(:debug).with("Value of 'preferred_serialization_format' (unsupported) is invalid for FormatTester, using default (two)") + Puppet.expects(:debug).with("FormatTester supports formats: one two; using two") + FormatTester.supported_formats + end + end + end + + it "should return the first format as the default format" do + FormatTester.expects(:supported_formats).returns [:one, :two] + FormatTester.default_format.should == :one + end + + it "should be able to use a protected format for better logging on errors" do + Puppet::Network::FormatHandler.should respond_to(:protected_format) + end + + it "should delegate all methods from the informative format to the specified format" do + format = mock 'format' + format.stubs(:name).returns(:myformat) + Puppet::Network::FormatHandler.expects(:format).twice.with(:myformat).returns format + + format.expects(:render).with("foo").returns "yay" + Puppet::Network::FormatHandler.protected_format(:myformat).render("foo").should == "yay" + end + + it "should provide better logging if a failure is encountered when delegating from the informative format to the real format" do + format = mock 'format' + format.stubs(:name).returns(:myformat) + Puppet::Network::FormatHandler.expects(:format).twice.with(:myformat).returns format + + format.expects(:render).with("foo").raises "foo" + lambda { Puppet::Network::FormatHandler.protected_format(:myformat).render("foo") }.should raise_error(Puppet::Network::FormatHandler::FormatError) + end + + it "should raise an error if we couldn't find a format by name or mime-type" do + Puppet::Network::FormatHandler.stubs(:format).with(:myformat).returns nil + lambda { Puppet::Network::FormatHandler.protected_format(:myformat) }.should raise_error + end + + describe "when using formats" do + before do + @format = mock 'format' + @format.stubs(:supported?).returns true + @format.stubs(:name).returns :my_format + Puppet::Network::FormatHandler.stubs(:format).with(:my_format).returns @format + Puppet::Network::FormatHandler.stubs(:mime).with("text/myformat").returns @format + Puppet::Network::Format.stubs(:===).returns false + Puppet::Network::Format.stubs(:===).with(@format).returns true + end + + it "should be able to test whether a format is supported" do + FormatTester.should respond_to(:support_format?) + end + + it "should use the Format to determine whether a given format is supported" do + @format.expects(:supported?).with(FormatTester) + FormatTester.support_format?(:my_format) + end + + it "should be able to convert from a given format" do + FormatTester.should respond_to(:convert_from) + end + + it "should call the format-specific converter when asked to convert from a given format" do + @format.expects(:intern).with(FormatTester, "mydata") + FormatTester.convert_from(:my_format, "mydata") + end + + it "should call the format-specific converter when asked to convert from a given format by mime-type" do + @format.expects(:intern).with(FormatTester, "mydata") + FormatTester.convert_from("text/myformat", "mydata") + end + + it "should call the format-specific converter when asked to convert from a given format by format instance" do + @format.expects(:intern).with(FormatTester, "mydata") + FormatTester.convert_from(@format, "mydata") + end + + it "should raise a FormatError when an exception is encountered when converting from a format" do + @format.expects(:intern).with(FormatTester, "mydata").raises "foo" + lambda { FormatTester.convert_from(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) + end + + it "should be able to use a specific hook for converting into multiple instances" do + @format.expects(:intern_multiple).with(FormatTester, "mydata") + + FormatTester.convert_from_multiple(:my_format, "mydata") + end + + it "should raise a FormatError when an exception is encountered when converting multiple items from a format" do + @format.expects(:intern_multiple).with(FormatTester, "mydata").raises "foo" + lambda { FormatTester.convert_from_multiple(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) + end + + it "should be able to use a specific hook for rendering multiple instances" do + @format.expects(:render_multiple).with("mydata") + + FormatTester.render_multiple(:my_format, "mydata") + end + + it "should raise a FormatError when an exception is encountered when rendering multiple items into a format" do + @format.expects(:render_multiple).with("mydata").raises "foo" + lambda { FormatTester.render_multiple(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) + end + end + + describe "when managing formats" do + it "should have a method for defining a new format" do + Puppet::Network::FormatHandler.should respond_to(:create) + end + + it "should create a format instance when asked" do + format = stub 'format', :name => :foo + Puppet::Network::Format.expects(:new).with(:foo).returns format + Puppet::Network::FormatHandler.create(:foo) + end + + it "should instance_eval any block provided when creating a format" do + format = stub 'format', :name => :instance_eval + format.expects(:yayness) + Puppet::Network::Format.expects(:new).returns format + Puppet::Network::FormatHandler.create(:instance_eval) do + yayness + end + end + + it "should be able to retrieve a format by name" do + format = Puppet::Network::FormatHandler.create(:by_name) + Puppet::Network::FormatHandler.format(:by_name).should equal(format) + end + + it "should be able to retrieve a format by extension" do + format = Puppet::Network::FormatHandler.create(:by_extension, :extension => "foo") + Puppet::Network::FormatHandler.format_by_extension("foo").should equal(format) + end + + it "should return nil if asked to return a format by an unknown extension" do + Puppet::Network::FormatHandler.format_by_extension("yayness").should be_nil + end + + it "should be able to retrieve formats by name irrespective of case and class" do + format = Puppet::Network::FormatHandler.create(:by_name) + Puppet::Network::FormatHandler.format(:By_Name).should equal(format) + end + + it "should be able to retrieve a format by mime type" do + format = Puppet::Network::FormatHandler.create(:by_name, :mime => "foo/bar") + Puppet::Network::FormatHandler.mime("foo/bar").should equal(format) + end + + it "should be able to retrieve a format by mime type irrespective of case" do + format = Puppet::Network::FormatHandler.create(:by_name, :mime => "foo/bar") + Puppet::Network::FormatHandler.mime("Foo/Bar").should equal(format) + end + + it "should be able to return all formats" do + one = stub 'one', :name => :one + two = stub 'two', :name => :two + Puppet::Network::Format.expects(:new).with(:one).returns(one) + Puppet::Network::Format.expects(:new).with(:two).returns(two) + + Puppet::Network::FormatHandler.create(:one) + Puppet::Network::FormatHandler.create(:two) + + list = Puppet::Network::FormatHandler.formats + list.should be_include(:one) + list.should be_include(:two) + end + end + + describe "when an instance" do + it "should be able to test whether a format is supported" do + FormatTester.new.should respond_to(:support_format?) + end + + it "should be able to convert to a given format" do + FormatTester.new.should respond_to(:render) + end + + it "should be able to get a format mime-type" do + FormatTester.new.should respond_to(:mime) + end + + it "should raise a FormatError when a rendering error is encountered" do + format = stub 'rendering format', :supported? => true, :name => :foo + Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format + + tester = FormatTester.new + format.expects(:render).with(tester).raises "eh" + + lambda { tester.render(:foo) }.should raise_error(Puppet::Network::FormatHandler::FormatError) + end + + it "should call the format-specific converter when asked to convert to a given format" do + format = stub 'rendering format', :supported? => true, :name => :foo + + Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format + + tester = FormatTester.new + format.expects(:render).with(tester).returns "foo" + + tester.render(:foo).should == "foo" + end + + it "should call the format-specific converter when asked to convert to a given format by mime-type" do + format = stub 'rendering format', :supported? => true, :name => :foo + Puppet::Network::FormatHandler.stubs(:mime).with("text/foo").returns format + Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format + + tester = FormatTester.new + format.expects(:render).with(tester).returns "foo" + + tester.render("text/foo").should == "foo" + end + + it "should call the format converter when asked to convert to a given format instance" do + format = stub 'rendering format', :supported? => true, :name => :foo + Puppet::Network::Format.stubs(:===).with(format).returns(true) + Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format + + tester = FormatTester.new + format.expects(:render).with(tester).returns "foo" + + tester.render(format).should == "foo" + end + + it "should render to the default format if no format is provided when rendering" do + format = stub 'rendering format', :supported? => true, :name => :foo + Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format + + FormatTester.expects(:default_format).returns :foo + tester = FormatTester.new + + format.expects(:render).with(tester) + tester.render + end + + it "should call the format-specific converter when asked for the mime-type of a given format" do + format = stub 'rendering format', :supported? => true, :name => :foo + + Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format + + tester = FormatTester.new + format.expects(:mime).returns "text/foo" + + tester.mime(:foo).should == "text/foo" + end + + it "should return the default format mime-type if no format is provided" do + format = stub 'rendering format', :supported? => true, :name => :foo + Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format + + FormatTester.expects(:default_format).returns :foo + tester = FormatTester.new + + format.expects(:mime).returns "text/foo" + tester.mime.should == "text/foo" + end + end +end diff --git a/spec/unit/network/format_handler_spec_spec.rb b/spec/unit/network/format_handler_spec_spec.rb deleted file mode 100755 index 622f7e5a1..000000000 --- a/spec/unit/network/format_handler_spec_spec.rb +++ /dev/null @@ -1,336 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/format_handler' - -class FormatTester - extend Puppet::Network::FormatHandler -end - -describe Puppet::Network::FormatHandler do - after do - formats = Puppet::Network::FormatHandler.instance_variable_get("@formats") - formats.each do |name, format| - formats.delete(name) unless format.is_a?(Puppet::Network::Format) - end - end - - it "should be able to list supported formats" do - FormatTester.should respond_to(:supported_formats) - end - - it "should include all supported formats" do - one = stub 'supported', :supported? => true, :name => :one, :weight => 1 - two = stub 'supported', :supported? => false, :name => :two, :weight => 1 - three = stub 'supported', :supported? => true, :name => :three, :weight => 1 - four = stub 'supported', :supported? => false, :name => :four, :weight => 1 - Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two, :three, :four] - Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one - Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two - Puppet::Network::FormatHandler.stubs(:format).with(:three).returns three - Puppet::Network::FormatHandler.stubs(:format).with(:four).returns four - result = FormatTester.supported_formats - result.length.should == 2 - result.should be_include(:one) - result.should be_include(:three) - end - - it "should return the supported formats in decreasing order of weight" do - one = stub 'supported', :supported? => true, :name => :one, :weight => 1 - two = stub 'supported', :supported? => true, :name => :two, :weight => 6 - three = stub 'supported', :supported? => true, :name => :three, :weight => 2 - four = stub 'supported', :supported? => true, :name => :four, :weight => 8 - Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two, :three, :four] - Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one - Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two - Puppet::Network::FormatHandler.stubs(:format).with(:three).returns three - Puppet::Network::FormatHandler.stubs(:format).with(:four).returns four - FormatTester.supported_formats.should == [:four, :two, :three, :one] - end - - - describe "with a preferred serialization format setting" do - before do - one = stub 'supported', :supported? => true, :name => :one, :weight => 1 - two = stub 'supported', :supported? => true, :name => :two, :weight => 6 - Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two] - Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one - Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two - end - describe "that is supported" do - before do - Puppet.settings.expects(:value).with(:preferred_serialization_format).returns :one - end - it "should return the preferred serialization format first" do - FormatTester.supported_formats.should == [:one, :two] - end - end - describe "that is not supported" do - before do - Puppet.settings.expects(:value).with(:preferred_serialization_format).returns :unsupported - end - it "should still return the default format first" do - FormatTester.supported_formats.should == [:two, :one] - end - it "should log a debug message" do - Puppet.expects(:debug).with("Value of 'preferred_serialization_format' (unsupported) is invalid for FormatTester, using default (two)") - Puppet.expects(:debug).with("FormatTester supports formats: one two; using two") - FormatTester.supported_formats - end - end - end - - it "should return the first format as the default format" do - FormatTester.expects(:supported_formats).returns [:one, :two] - FormatTester.default_format.should == :one - end - - it "should be able to use a protected format for better logging on errors" do - Puppet::Network::FormatHandler.should respond_to(:protected_format) - end - - it "should delegate all methods from the informative format to the specified format" do - format = mock 'format' - format.stubs(:name).returns(:myformat) - Puppet::Network::FormatHandler.expects(:format).twice.with(:myformat).returns format - - format.expects(:render).with("foo").returns "yay" - Puppet::Network::FormatHandler.protected_format(:myformat).render("foo").should == "yay" - end - - it "should provide better logging if a failure is encountered when delegating from the informative format to the real format" do - format = mock 'format' - format.stubs(:name).returns(:myformat) - Puppet::Network::FormatHandler.expects(:format).twice.with(:myformat).returns format - - format.expects(:render).with("foo").raises "foo" - lambda { Puppet::Network::FormatHandler.protected_format(:myformat).render("foo") }.should raise_error(Puppet::Network::FormatHandler::FormatError) - end - - it "should raise an error if we couldn't find a format by name or mime-type" do - Puppet::Network::FormatHandler.stubs(:format).with(:myformat).returns nil - lambda { Puppet::Network::FormatHandler.protected_format(:myformat) }.should raise_error - end - - describe "when using formats" do - before do - @format = mock 'format' - @format.stubs(:supported?).returns true - @format.stubs(:name).returns :my_format - Puppet::Network::FormatHandler.stubs(:format).with(:my_format).returns @format - Puppet::Network::FormatHandler.stubs(:mime).with("text/myformat").returns @format - Puppet::Network::Format.stubs(:===).returns false - Puppet::Network::Format.stubs(:===).with(@format).returns true - end - - it "should be able to test whether a format is supported" do - FormatTester.should respond_to(:support_format?) - end - - it "should use the Format to determine whether a given format is supported" do - @format.expects(:supported?).with(FormatTester) - FormatTester.support_format?(:my_format) - end - - it "should be able to convert from a given format" do - FormatTester.should respond_to(:convert_from) - end - - it "should call the format-specific converter when asked to convert from a given format" do - @format.expects(:intern).with(FormatTester, "mydata") - FormatTester.convert_from(:my_format, "mydata") - end - - it "should call the format-specific converter when asked to convert from a given format by mime-type" do - @format.expects(:intern).with(FormatTester, "mydata") - FormatTester.convert_from("text/myformat", "mydata") - end - - it "should call the format-specific converter when asked to convert from a given format by format instance" do - @format.expects(:intern).with(FormatTester, "mydata") - FormatTester.convert_from(@format, "mydata") - end - - it "should raise a FormatError when an exception is encountered when converting from a format" do - @format.expects(:intern).with(FormatTester, "mydata").raises "foo" - lambda { FormatTester.convert_from(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) - end - - it "should be able to use a specific hook for converting into multiple instances" do - @format.expects(:intern_multiple).with(FormatTester, "mydata") - - FormatTester.convert_from_multiple(:my_format, "mydata") - end - - it "should raise a FormatError when an exception is encountered when converting multiple items from a format" do - @format.expects(:intern_multiple).with(FormatTester, "mydata").raises "foo" - lambda { FormatTester.convert_from_multiple(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) - end - - it "should be able to use a specific hook for rendering multiple instances" do - @format.expects(:render_multiple).with("mydata") - - FormatTester.render_multiple(:my_format, "mydata") - end - - it "should raise a FormatError when an exception is encountered when rendering multiple items into a format" do - @format.expects(:render_multiple).with("mydata").raises "foo" - lambda { FormatTester.render_multiple(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) - end - end - - describe "when managing formats" do - it "should have a method for defining a new format" do - Puppet::Network::FormatHandler.should respond_to(:create) - end - - it "should create a format instance when asked" do - format = stub 'format', :name => :foo - Puppet::Network::Format.expects(:new).with(:foo).returns format - Puppet::Network::FormatHandler.create(:foo) - end - - it "should instance_eval any block provided when creating a format" do - format = stub 'format', :name => :instance_eval - format.expects(:yayness) - Puppet::Network::Format.expects(:new).returns format - Puppet::Network::FormatHandler.create(:instance_eval) do - yayness - end - end - - it "should be able to retrieve a format by name" do - format = Puppet::Network::FormatHandler.create(:by_name) - Puppet::Network::FormatHandler.format(:by_name).should equal(format) - end - - it "should be able to retrieve a format by extension" do - format = Puppet::Network::FormatHandler.create(:by_extension, :extension => "foo") - Puppet::Network::FormatHandler.format_by_extension("foo").should equal(format) - end - - it "should return nil if asked to return a format by an unknown extension" do - Puppet::Network::FormatHandler.format_by_extension("yayness").should be_nil - end - - it "should be able to retrieve formats by name irrespective of case and class" do - format = Puppet::Network::FormatHandler.create(:by_name) - Puppet::Network::FormatHandler.format(:By_Name).should equal(format) - end - - it "should be able to retrieve a format by mime type" do - format = Puppet::Network::FormatHandler.create(:by_name, :mime => "foo/bar") - Puppet::Network::FormatHandler.mime("foo/bar").should equal(format) - end - - it "should be able to retrieve a format by mime type irrespective of case" do - format = Puppet::Network::FormatHandler.create(:by_name, :mime => "foo/bar") - Puppet::Network::FormatHandler.mime("Foo/Bar").should equal(format) - end - - it "should be able to return all formats" do - one = stub 'one', :name => :one - two = stub 'two', :name => :two - Puppet::Network::Format.expects(:new).with(:one).returns(one) - Puppet::Network::Format.expects(:new).with(:two).returns(two) - - Puppet::Network::FormatHandler.create(:one) - Puppet::Network::FormatHandler.create(:two) - - list = Puppet::Network::FormatHandler.formats - list.should be_include(:one) - list.should be_include(:two) - end - end - - describe "when an instance" do - it "should be able to test whether a format is supported" do - FormatTester.new.should respond_to(:support_format?) - end - - it "should be able to convert to a given format" do - FormatTester.new.should respond_to(:render) - end - - it "should be able to get a format mime-type" do - FormatTester.new.should respond_to(:mime) - end - - it "should raise a FormatError when a rendering error is encountered" do - format = stub 'rendering format', :supported? => true, :name => :foo - Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format - - tester = FormatTester.new - format.expects(:render).with(tester).raises "eh" - - lambda { tester.render(:foo) }.should raise_error(Puppet::Network::FormatHandler::FormatError) - end - - it "should call the format-specific converter when asked to convert to a given format" do - format = stub 'rendering format', :supported? => true, :name => :foo - - Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format - - tester = FormatTester.new - format.expects(:render).with(tester).returns "foo" - - tester.render(:foo).should == "foo" - end - - it "should call the format-specific converter when asked to convert to a given format by mime-type" do - format = stub 'rendering format', :supported? => true, :name => :foo - Puppet::Network::FormatHandler.stubs(:mime).with("text/foo").returns format - Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format - - tester = FormatTester.new - format.expects(:render).with(tester).returns "foo" - - tester.render("text/foo").should == "foo" - end - - it "should call the format converter when asked to convert to a given format instance" do - format = stub 'rendering format', :supported? => true, :name => :foo - Puppet::Network::Format.stubs(:===).with(format).returns(true) - Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format - - tester = FormatTester.new - format.expects(:render).with(tester).returns "foo" - - tester.render(format).should == "foo" - end - - it "should render to the default format if no format is provided when rendering" do - format = stub 'rendering format', :supported? => true, :name => :foo - Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format - - FormatTester.expects(:default_format).returns :foo - tester = FormatTester.new - - format.expects(:render).with(tester) - tester.render - end - - it "should call the format-specific converter when asked for the mime-type of a given format" do - format = stub 'rendering format', :supported? => true, :name => :foo - - Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format - - tester = FormatTester.new - format.expects(:mime).returns "text/foo" - - tester.mime(:foo).should == "text/foo" - end - - it "should return the default format mime-type if no format is provided" do - format = stub 'rendering format', :supported? => true, :name => :foo - Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format - - FormatTester.expects(:default_format).returns :foo - tester = FormatTester.new - - format.expects(:mime).returns "text/foo" - tester.mime.should == "text/foo" - end - end -end diff --git a/spec/unit/network/format_spec.rb b/spec/unit/network/format_spec.rb new file mode 100755 index 000000000..73530b9d7 --- /dev/null +++ b/spec/unit/network/format_spec.rb @@ -0,0 +1,198 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/format' + +# A class with all of the necessary +# hooks. +class FormatRenderer + def self.to_multiple_my_format(list) + end + + def self.from_multiple_my_format(text) + end + + def self.from_my_format(text) + end + + def to_my_format + end +end + +describe Puppet::Network::Format do + describe "when initializing" do + it "should require a name" do + lambda { Puppet::Network::Format.new }.should raise_error(ArgumentError) + end + + it "should be able to provide its name" do + Puppet::Network::Format.new(:my_format).name.should == :my_format + end + + it "should always convert its name to a downcased symbol" do + Puppet::Network::Format.new(:My_Format).name.should == :my_format + end + + it "should be able to set its downcased mime type at initialization" do + format = Puppet::Network::Format.new(:my_format, :mime => "Foo/Bar") + format.mime.should == "foo/bar" + end + + it "should default to text plus the name of the format as the mime type" do + Puppet::Network::Format.new(:my_format).mime.should == "text/my_format" + end + + it "should fail if unsupported options are provided" do + lambda { Puppet::Network::Format.new(:my_format, :foo => "bar") }.should raise_error(ArgumentError) + end + end + + describe "instances" do + before do + @format = Puppet::Network::Format.new(:my_format) + end + + it "should support being confined" do + @format.should respond_to(:confine) + end + + it "should not be considered suitable if confinement conditions are not met" do + @format.confine :true => false + @format.should_not be_suitable + end + + it "should be able to determine if a class is supported" do + @format.should respond_to(:supported?) + end + + it "should consider a class to be supported if it has the individual and multiple methods for rendering and interning" do + @format.should be_supported(FormatRenderer) + end + + it "should default to its required methods being the individual and multiple methods for rendering and interning" do + Puppet::Network::Format.new(:foo).required_methods.sort { |a,b| a.to_s <=> b.to_s }.should == [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].sort { |a,b| a.to_s <=> b.to_s } + end + + it "should consider a class supported if the provided class has all required methods present" do + format = Puppet::Network::Format.new(:foo) + [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].each do |method| + format.expects(:required_method_present?).with { |name, klass, type| name == method and klass == String }.returns true + end + + format.should be_required_methods_present(String) + end + + it "should consider a class not supported if any required methods are missing from the provided class" do + format = Puppet::Network::Format.new(:foo) + format.stubs(:required_method_present?).returns true + format.expects(:required_method_present?).with { |name, *args| name == :intern_method }.returns false + format.should_not be_required_methods_present(String) + end + + it "should be able to specify the methods required for support" do + Puppet::Network::Format.new(:foo, :required_methods => [:render_method, :intern_method]).required_methods.should == [:render_method, :intern_method] + end + + it "should only test for required methods if specific methods are specified as required" do + format = Puppet::Network::Format.new(:foo, :required_methods => [:intern_method]) + format.expects(:required_method_present?).with { |name, klass, type| name == :intern_method } + + format.required_methods_present?(String) + end + + it "should not consider a class supported unless the format is suitable" do + @format.expects(:suitable?).returns false + @format.should_not be_supported(FormatRenderer) + end + + it "should always downcase mimetypes" do + @format.mime = "Foo/Bar" + @format.mime.should == "foo/bar" + end + + it "should support having a weight" do + @format.should respond_to(:weight) + end + + it "should default to a weight of of 5" do + @format.weight.should == 5 + end + + it "should be able to override its weight at initialization" do + Puppet::Network::Format.new(:foo, :weight => 1).weight.should == 1 + end + + it "should default to its extension being equal to its name" do + Puppet::Network::Format.new(:foo).extension.should == "foo" + end + + it "should support overriding the extension" do + Puppet::Network::Format.new(:foo, :extension => "bar").extension.should == "bar" + end + [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].each do |method| + it "should allow assignment of the #{method}" do + Puppet::Network::Format.new(:foo, method => :foo).send(method).should == :foo + end + end + end + + describe "when converting between instances and formatted text" do + before do + @format = Puppet::Network::Format.new(:my_format) + @instance = FormatRenderer.new + end + + it "should have a method for rendering a single instance" do + @format.should respond_to(:render) + end + + it "should have a method for rendering multiple instances" do + @format.should respond_to(:render_multiple) + end + + it "should have a method for interning text" do + @format.should respond_to(:intern) + end + + it "should have a method for interning text into multiple instances" do + @format.should respond_to(:intern_multiple) + end + + it "should return the results of calling the instance-specific render method if the method is present" do + @instance.expects(:to_my_format).returns "foo" + @format.render(@instance).should == "foo" + end + + it "should return the results of calling the class-specific render_multiple method if the method is present" do + @instance.class.expects(:to_multiple_my_format).returns ["foo"] + @format.render_multiple([@instance]).should == ["foo"] + end + + it "should return the results of calling the class-specific intern method if the method is present" do + FormatRenderer.expects(:from_my_format).with("foo").returns @instance + @format.intern(FormatRenderer, "foo").should equal(@instance) + end + + it "should return the results of calling the class-specific intern_multiple method if the method is present" do + FormatRenderer.expects(:from_multiple_my_format).with("foo").returns [@instance] + @format.intern_multiple(FormatRenderer, "foo").should == [@instance] + end + + it "should fail if asked to render and the instance does not respond to 'to_'" do + lambda { @format.render("foo") }.should raise_error(NotImplementedError) + end + + it "should fail if asked to intern and the class does not respond to 'from_'" do + lambda { @format.intern(String, "foo") }.should raise_error(NotImplementedError) + end + + it "should fail if asked to intern multiple and the class does not respond to 'from_multiple_'" do + lambda { @format.intern_multiple(String, "foo") }.should raise_error(NotImplementedError) + end + + it "should fail if asked to render multiple and the instance does not respond to 'to_multiple_'" do + lambda { @format.render_multiple(["foo", "bar"]) }.should raise_error(NotImplementedError) + end + end +end diff --git a/spec/unit/network/format_spec_spec.rb b/spec/unit/network/format_spec_spec.rb deleted file mode 100755 index 73530b9d7..000000000 --- a/spec/unit/network/format_spec_spec.rb +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/format' - -# A class with all of the necessary -# hooks. -class FormatRenderer - def self.to_multiple_my_format(list) - end - - def self.from_multiple_my_format(text) - end - - def self.from_my_format(text) - end - - def to_my_format - end -end - -describe Puppet::Network::Format do - describe "when initializing" do - it "should require a name" do - lambda { Puppet::Network::Format.new }.should raise_error(ArgumentError) - end - - it "should be able to provide its name" do - Puppet::Network::Format.new(:my_format).name.should == :my_format - end - - it "should always convert its name to a downcased symbol" do - Puppet::Network::Format.new(:My_Format).name.should == :my_format - end - - it "should be able to set its downcased mime type at initialization" do - format = Puppet::Network::Format.new(:my_format, :mime => "Foo/Bar") - format.mime.should == "foo/bar" - end - - it "should default to text plus the name of the format as the mime type" do - Puppet::Network::Format.new(:my_format).mime.should == "text/my_format" - end - - it "should fail if unsupported options are provided" do - lambda { Puppet::Network::Format.new(:my_format, :foo => "bar") }.should raise_error(ArgumentError) - end - end - - describe "instances" do - before do - @format = Puppet::Network::Format.new(:my_format) - end - - it "should support being confined" do - @format.should respond_to(:confine) - end - - it "should not be considered suitable if confinement conditions are not met" do - @format.confine :true => false - @format.should_not be_suitable - end - - it "should be able to determine if a class is supported" do - @format.should respond_to(:supported?) - end - - it "should consider a class to be supported if it has the individual and multiple methods for rendering and interning" do - @format.should be_supported(FormatRenderer) - end - - it "should default to its required methods being the individual and multiple methods for rendering and interning" do - Puppet::Network::Format.new(:foo).required_methods.sort { |a,b| a.to_s <=> b.to_s }.should == [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].sort { |a,b| a.to_s <=> b.to_s } - end - - it "should consider a class supported if the provided class has all required methods present" do - format = Puppet::Network::Format.new(:foo) - [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].each do |method| - format.expects(:required_method_present?).with { |name, klass, type| name == method and klass == String }.returns true - end - - format.should be_required_methods_present(String) - end - - it "should consider a class not supported if any required methods are missing from the provided class" do - format = Puppet::Network::Format.new(:foo) - format.stubs(:required_method_present?).returns true - format.expects(:required_method_present?).with { |name, *args| name == :intern_method }.returns false - format.should_not be_required_methods_present(String) - end - - it "should be able to specify the methods required for support" do - Puppet::Network::Format.new(:foo, :required_methods => [:render_method, :intern_method]).required_methods.should == [:render_method, :intern_method] - end - - it "should only test for required methods if specific methods are specified as required" do - format = Puppet::Network::Format.new(:foo, :required_methods => [:intern_method]) - format.expects(:required_method_present?).with { |name, klass, type| name == :intern_method } - - format.required_methods_present?(String) - end - - it "should not consider a class supported unless the format is suitable" do - @format.expects(:suitable?).returns false - @format.should_not be_supported(FormatRenderer) - end - - it "should always downcase mimetypes" do - @format.mime = "Foo/Bar" - @format.mime.should == "foo/bar" - end - - it "should support having a weight" do - @format.should respond_to(:weight) - end - - it "should default to a weight of of 5" do - @format.weight.should == 5 - end - - it "should be able to override its weight at initialization" do - Puppet::Network::Format.new(:foo, :weight => 1).weight.should == 1 - end - - it "should default to its extension being equal to its name" do - Puppet::Network::Format.new(:foo).extension.should == "foo" - end - - it "should support overriding the extension" do - Puppet::Network::Format.new(:foo, :extension => "bar").extension.should == "bar" - end - [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].each do |method| - it "should allow assignment of the #{method}" do - Puppet::Network::Format.new(:foo, method => :foo).send(method).should == :foo - end - end - end - - describe "when converting between instances and formatted text" do - before do - @format = Puppet::Network::Format.new(:my_format) - @instance = FormatRenderer.new - end - - it "should have a method for rendering a single instance" do - @format.should respond_to(:render) - end - - it "should have a method for rendering multiple instances" do - @format.should respond_to(:render_multiple) - end - - it "should have a method for interning text" do - @format.should respond_to(:intern) - end - - it "should have a method for interning text into multiple instances" do - @format.should respond_to(:intern_multiple) - end - - it "should return the results of calling the instance-specific render method if the method is present" do - @instance.expects(:to_my_format).returns "foo" - @format.render(@instance).should == "foo" - end - - it "should return the results of calling the class-specific render_multiple method if the method is present" do - @instance.class.expects(:to_multiple_my_format).returns ["foo"] - @format.render_multiple([@instance]).should == ["foo"] - end - - it "should return the results of calling the class-specific intern method if the method is present" do - FormatRenderer.expects(:from_my_format).with("foo").returns @instance - @format.intern(FormatRenderer, "foo").should equal(@instance) - end - - it "should return the results of calling the class-specific intern_multiple method if the method is present" do - FormatRenderer.expects(:from_multiple_my_format).with("foo").returns [@instance] - @format.intern_multiple(FormatRenderer, "foo").should == [@instance] - end - - it "should fail if asked to render and the instance does not respond to 'to_'" do - lambda { @format.render("foo") }.should raise_error(NotImplementedError) - end - - it "should fail if asked to intern and the class does not respond to 'from_'" do - lambda { @format.intern(String, "foo") }.should raise_error(NotImplementedError) - end - - it "should fail if asked to intern multiple and the class does not respond to 'from_multiple_'" do - lambda { @format.intern_multiple(String, "foo") }.should raise_error(NotImplementedError) - end - - it "should fail if asked to render multiple and the instance does not respond to 'to_multiple_'" do - lambda { @format.render_multiple(["foo", "bar"]) }.should raise_error(NotImplementedError) - end - end -end diff --git a/spec/unit/network/formats_spec.rb b/spec/unit/network/formats_spec.rb new file mode 100755 index 000000000..fd78ebf52 --- /dev/null +++ b/spec/unit/network/formats_spec.rb @@ -0,0 +1,337 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/formats' + +class PsonTest + attr_accessor :string + def ==(other) + string == other.string + end + + def self.from_pson(data) + new(data) + end + + def initialize(string) + @string = string + end + + def to_pson(*args) + { + 'type' => self.class.name, + 'data' => @string + }.to_pson(*args) + end +end + +describe "Puppet Network Format" do + it "should include a yaml format" do + Puppet::Network::FormatHandler.format(:yaml).should_not be_nil + end + + describe "yaml" do + before do + @yaml = Puppet::Network::FormatHandler.format(:yaml) + end + + it "should have its mime type set to text/yaml" do + @yaml.mime.should == "text/yaml" + end + + it "should be supported on Strings" do + @yaml.should be_supported(String) + end + + it "should render by calling 'to_yaml' on the instance" do + instance = mock 'instance' + instance.expects(:to_yaml).returns "foo" + @yaml.render(instance).should == "foo" + end + + it "should render multiple instances by calling 'to_yaml' on the array" do + instances = [mock('instance')] + instances.expects(:to_yaml).returns "foo" + @yaml.render_multiple(instances).should == "foo" + end + + it "should intern by calling 'YAML.load'" do + text = "foo" + YAML.expects(:load).with("foo").returns "bar" + @yaml.intern(String, text).should == "bar" + end + + it "should intern multiples by calling 'YAML.load'" do + text = "foo" + YAML.expects(:load).with("foo").returns "bar" + @yaml.intern_multiple(String, text).should == "bar" + end + end + + describe "base64 compressed yaml" do + yaml = Puppet::Network::FormatHandler.format(:b64_zlib_yaml) + confine "We must have zlib" => Puppet.features.zlib? + + before do + @yaml = Puppet::Network::FormatHandler.format(:b64_zlib_yaml) + end + + it "should have its mime type set to text/b64_zlib_yaml" do + @yaml.mime.should == "text/b64_zlib_yaml" + end + + it "should render by calling 'to_yaml' on the instance" do + instance = mock 'instance' + instance.expects(:to_yaml).returns "foo" + @yaml.render(instance) + end + + it "should encode generated yaml on render" do + instance = mock 'instance', :to_yaml => "foo" + + @yaml.expects(:encode).with("foo").returns "bar" + + @yaml.render(instance).should == "bar" + end + + it "should render multiple instances by calling 'to_yaml' on the array" do + instances = [mock('instance')] + instances.expects(:to_yaml).returns "foo" + @yaml.render_multiple(instances) + end + + it "should encode generated yaml on render" do + instances = [mock('instance')] + instances.stubs(:to_yaml).returns "foo" + + @yaml.expects(:encode).with("foo").returns "bar" + + @yaml.render(instances).should == "bar" + end + + it "should intern by calling decode" do + text = "foo" + @yaml.expects(:decode).with("foo").returns "bar" + @yaml.intern(String, text).should == "bar" + end + + it "should intern multiples by calling 'decode'" do + text = "foo" + @yaml.expects(:decode).with("foo").returns "bar" + @yaml.intern_multiple(String, text).should == "bar" + end + + it "should decode by base64 decoding, uncompressing and Yaml loading" do + Base64.expects(:decode64).with("zorg").returns "foo" + Zlib::Inflate.expects(:inflate).with("foo").returns "baz" + YAML.expects(:load).with("baz").returns "bar" + @yaml.decode("zorg").should == "bar" + end + + it "should encode by compressing and base64 encoding" do + Zlib::Deflate.expects(:deflate).with("foo", Zlib::BEST_COMPRESSION).returns "bar" + Base64.expects(:encode64).with("bar").returns "baz" + @yaml.encode("foo").should == "baz" + end + + describe "when zlib is disabled" do + before do + Puppet[:zlib] = false + end + + it "use_zlib? should return false" do + @yaml.use_zlib?.should == false + end + + it "should refuse to encode" do + lambda{ @yaml.encode("foo") }.should raise_error + end + + it "should refuse to decode" do + lambda{ @yaml.decode("foo") }.should raise_error + end + end + + describe "when zlib is not installed" do + it "use_zlib? should return false" do + Puppet[:zlib] = true + Puppet.features.expects(:zlib?).returns(false) + + @yaml.use_zlib?.should == false + end + end + + end + + it "should include a marshal format" do + Puppet::Network::FormatHandler.format(:marshal).should_not be_nil + end + + describe "marshal" do + before do + @marshal = Puppet::Network::FormatHandler.format(:marshal) + end + + it "should have its mime type set to text/marshal" do + Puppet::Network::FormatHandler.format(:marshal).mime.should == "text/marshal" + end + + it "should be supported on Strings" do + @marshal.should be_supported(String) + end + + it "should render by calling 'Marshal.dump' on the instance" do + instance = mock 'instance' + Marshal.expects(:dump).with(instance).returns "foo" + @marshal.render(instance).should == "foo" + end + + it "should render multiple instances by calling 'to_marshal' on the array" do + instances = [mock('instance')] + + Marshal.expects(:dump).with(instances).returns "foo" + @marshal.render_multiple(instances).should == "foo" + end + + it "should intern by calling 'Marshal.load'" do + text = "foo" + Marshal.expects(:load).with("foo").returns "bar" + @marshal.intern(String, text).should == "bar" + end + + it "should intern multiples by calling 'Marshal.load'" do + text = "foo" + Marshal.expects(:load).with("foo").returns "bar" + @marshal.intern_multiple(String, text).should == "bar" + end + end + + describe "plaintext" do + before do + @text = Puppet::Network::FormatHandler.format(:s) + end + + it "should have its mimetype set to text/plain" do + @text.mime.should == "text/plain" + end + + it "should use 'txt' as its extension" do + @text.extension.should == "txt" + end + end + + describe "dot" do + before do + @dot = Puppet::Network::FormatHandler.format(:dot) + end + + it "should have its mimetype set to text/dot" do + @dot.mime.should == "text/dot" + end + end + + describe Puppet::Network::FormatHandler.format(:raw) do + before do + @format = Puppet::Network::FormatHandler.format(:raw) + end + + it "should exist" do + @format.should_not be_nil + end + + it "should have its mimetype set to application/x-raw" do + @format.mime.should == "application/x-raw" + end + + it "should always be supported" do + @format.should be_supported(String) + end + + it "should fail if its multiple_render method is used" do + lambda { @format.render_multiple("foo") }.should raise_error(NotImplementedError) + end + + it "should fail if its multiple_intern method is used" do + lambda { @format.intern_multiple(String, "foo") }.should raise_error(NotImplementedError) + end + + it "should have a weight of 1" do + @format.weight.should == 1 + end + end + + it "should include a pson format" do + Puppet::Network::FormatHandler.format(:pson).should_not be_nil + end + + describe "pson" do + confine "Missing 'pson' library" => Puppet.features.pson? + + before do + @pson = Puppet::Network::FormatHandler.format(:pson) + end + + it "should have its mime type set to text/pson" do + Puppet::Network::FormatHandler.format(:pson).mime.should == "text/pson" + end + + it "should require the :render_method" do + Puppet::Network::FormatHandler.format(:pson).required_methods.should be_include(:render_method) + end + + it "should require the :intern_method" do + Puppet::Network::FormatHandler.format(:pson).required_methods.should be_include(:intern_method) + end + + it "should have a weight of 10" do + @pson.weight.should == 10 + end + + describe "when supported" do + it "should render by calling 'to_pson' on the instance" do + instance = PsonTest.new("foo") + instance.expects(:to_pson).returns "foo" + @pson.render(instance).should == "foo" + end + + it "should render multiple instances by calling 'to_pson' on the array" do + instances = [mock('instance')] + + instances.expects(:to_pson).returns "foo" + + @pson.render_multiple(instances).should == "foo" + end + + it "should intern by calling 'PSON.parse' on the text and then using from_pson to convert the data into an instance" do + text = "foo" + PSON.expects(:parse).with("foo").returns("type" => "PsonTest", "data" => "foo") + PsonTest.expects(:from_pson).with("foo").returns "parsed_pson" + @pson.intern(PsonTest, text).should == "parsed_pson" + end + + it "should not render twice if 'PSON.parse' creates the appropriate instance" do + text = "foo" + instance = PsonTest.new("foo") + PSON.expects(:parse).with("foo").returns(instance) + PsonTest.expects(:from_pson).never + @pson.intern(PsonTest, text).should equal(instance) + end + + it "should intern by calling 'PSON.parse' on the text and then using from_pson to convert the actual into an instance if the pson has no class/data separation" do + text = "foo" + PSON.expects(:parse).with("foo").returns("foo") + PsonTest.expects(:from_pson).with("foo").returns "parsed_pson" + @pson.intern(PsonTest, text).should == "parsed_pson" + end + + it "should intern multiples by parsing the text and using 'class.intern' on each resulting data structure" do + text = "foo" + PSON.expects(:parse).with("foo").returns ["bar", "baz"] + PsonTest.expects(:from_pson).with("bar").returns "BAR" + PsonTest.expects(:from_pson).with("baz").returns "BAZ" + @pson.intern_multiple(PsonTest, text).should == %w{BAR BAZ} + end + end + end +end diff --git a/spec/unit/network/formats_spec_spec.rb b/spec/unit/network/formats_spec_spec.rb deleted file mode 100755 index fd78ebf52..000000000 --- a/spec/unit/network/formats_spec_spec.rb +++ /dev/null @@ -1,337 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/formats' - -class PsonTest - attr_accessor :string - def ==(other) - string == other.string - end - - def self.from_pson(data) - new(data) - end - - def initialize(string) - @string = string - end - - def to_pson(*args) - { - 'type' => self.class.name, - 'data' => @string - }.to_pson(*args) - end -end - -describe "Puppet Network Format" do - it "should include a yaml format" do - Puppet::Network::FormatHandler.format(:yaml).should_not be_nil - end - - describe "yaml" do - before do - @yaml = Puppet::Network::FormatHandler.format(:yaml) - end - - it "should have its mime type set to text/yaml" do - @yaml.mime.should == "text/yaml" - end - - it "should be supported on Strings" do - @yaml.should be_supported(String) - end - - it "should render by calling 'to_yaml' on the instance" do - instance = mock 'instance' - instance.expects(:to_yaml).returns "foo" - @yaml.render(instance).should == "foo" - end - - it "should render multiple instances by calling 'to_yaml' on the array" do - instances = [mock('instance')] - instances.expects(:to_yaml).returns "foo" - @yaml.render_multiple(instances).should == "foo" - end - - it "should intern by calling 'YAML.load'" do - text = "foo" - YAML.expects(:load).with("foo").returns "bar" - @yaml.intern(String, text).should == "bar" - end - - it "should intern multiples by calling 'YAML.load'" do - text = "foo" - YAML.expects(:load).with("foo").returns "bar" - @yaml.intern_multiple(String, text).should == "bar" - end - end - - describe "base64 compressed yaml" do - yaml = Puppet::Network::FormatHandler.format(:b64_zlib_yaml) - confine "We must have zlib" => Puppet.features.zlib? - - before do - @yaml = Puppet::Network::FormatHandler.format(:b64_zlib_yaml) - end - - it "should have its mime type set to text/b64_zlib_yaml" do - @yaml.mime.should == "text/b64_zlib_yaml" - end - - it "should render by calling 'to_yaml' on the instance" do - instance = mock 'instance' - instance.expects(:to_yaml).returns "foo" - @yaml.render(instance) - end - - it "should encode generated yaml on render" do - instance = mock 'instance', :to_yaml => "foo" - - @yaml.expects(:encode).with("foo").returns "bar" - - @yaml.render(instance).should == "bar" - end - - it "should render multiple instances by calling 'to_yaml' on the array" do - instances = [mock('instance')] - instances.expects(:to_yaml).returns "foo" - @yaml.render_multiple(instances) - end - - it "should encode generated yaml on render" do - instances = [mock('instance')] - instances.stubs(:to_yaml).returns "foo" - - @yaml.expects(:encode).with("foo").returns "bar" - - @yaml.render(instances).should == "bar" - end - - it "should intern by calling decode" do - text = "foo" - @yaml.expects(:decode).with("foo").returns "bar" - @yaml.intern(String, text).should == "bar" - end - - it "should intern multiples by calling 'decode'" do - text = "foo" - @yaml.expects(:decode).with("foo").returns "bar" - @yaml.intern_multiple(String, text).should == "bar" - end - - it "should decode by base64 decoding, uncompressing and Yaml loading" do - Base64.expects(:decode64).with("zorg").returns "foo" - Zlib::Inflate.expects(:inflate).with("foo").returns "baz" - YAML.expects(:load).with("baz").returns "bar" - @yaml.decode("zorg").should == "bar" - end - - it "should encode by compressing and base64 encoding" do - Zlib::Deflate.expects(:deflate).with("foo", Zlib::BEST_COMPRESSION).returns "bar" - Base64.expects(:encode64).with("bar").returns "baz" - @yaml.encode("foo").should == "baz" - end - - describe "when zlib is disabled" do - before do - Puppet[:zlib] = false - end - - it "use_zlib? should return false" do - @yaml.use_zlib?.should == false - end - - it "should refuse to encode" do - lambda{ @yaml.encode("foo") }.should raise_error - end - - it "should refuse to decode" do - lambda{ @yaml.decode("foo") }.should raise_error - end - end - - describe "when zlib is not installed" do - it "use_zlib? should return false" do - Puppet[:zlib] = true - Puppet.features.expects(:zlib?).returns(false) - - @yaml.use_zlib?.should == false - end - end - - end - - it "should include a marshal format" do - Puppet::Network::FormatHandler.format(:marshal).should_not be_nil - end - - describe "marshal" do - before do - @marshal = Puppet::Network::FormatHandler.format(:marshal) - end - - it "should have its mime type set to text/marshal" do - Puppet::Network::FormatHandler.format(:marshal).mime.should == "text/marshal" - end - - it "should be supported on Strings" do - @marshal.should be_supported(String) - end - - it "should render by calling 'Marshal.dump' on the instance" do - instance = mock 'instance' - Marshal.expects(:dump).with(instance).returns "foo" - @marshal.render(instance).should == "foo" - end - - it "should render multiple instances by calling 'to_marshal' on the array" do - instances = [mock('instance')] - - Marshal.expects(:dump).with(instances).returns "foo" - @marshal.render_multiple(instances).should == "foo" - end - - it "should intern by calling 'Marshal.load'" do - text = "foo" - Marshal.expects(:load).with("foo").returns "bar" - @marshal.intern(String, text).should == "bar" - end - - it "should intern multiples by calling 'Marshal.load'" do - text = "foo" - Marshal.expects(:load).with("foo").returns "bar" - @marshal.intern_multiple(String, text).should == "bar" - end - end - - describe "plaintext" do - before do - @text = Puppet::Network::FormatHandler.format(:s) - end - - it "should have its mimetype set to text/plain" do - @text.mime.should == "text/plain" - end - - it "should use 'txt' as its extension" do - @text.extension.should == "txt" - end - end - - describe "dot" do - before do - @dot = Puppet::Network::FormatHandler.format(:dot) - end - - it "should have its mimetype set to text/dot" do - @dot.mime.should == "text/dot" - end - end - - describe Puppet::Network::FormatHandler.format(:raw) do - before do - @format = Puppet::Network::FormatHandler.format(:raw) - end - - it "should exist" do - @format.should_not be_nil - end - - it "should have its mimetype set to application/x-raw" do - @format.mime.should == "application/x-raw" - end - - it "should always be supported" do - @format.should be_supported(String) - end - - it "should fail if its multiple_render method is used" do - lambda { @format.render_multiple("foo") }.should raise_error(NotImplementedError) - end - - it "should fail if its multiple_intern method is used" do - lambda { @format.intern_multiple(String, "foo") }.should raise_error(NotImplementedError) - end - - it "should have a weight of 1" do - @format.weight.should == 1 - end - end - - it "should include a pson format" do - Puppet::Network::FormatHandler.format(:pson).should_not be_nil - end - - describe "pson" do - confine "Missing 'pson' library" => Puppet.features.pson? - - before do - @pson = Puppet::Network::FormatHandler.format(:pson) - end - - it "should have its mime type set to text/pson" do - Puppet::Network::FormatHandler.format(:pson).mime.should == "text/pson" - end - - it "should require the :render_method" do - Puppet::Network::FormatHandler.format(:pson).required_methods.should be_include(:render_method) - end - - it "should require the :intern_method" do - Puppet::Network::FormatHandler.format(:pson).required_methods.should be_include(:intern_method) - end - - it "should have a weight of 10" do - @pson.weight.should == 10 - end - - describe "when supported" do - it "should render by calling 'to_pson' on the instance" do - instance = PsonTest.new("foo") - instance.expects(:to_pson).returns "foo" - @pson.render(instance).should == "foo" - end - - it "should render multiple instances by calling 'to_pson' on the array" do - instances = [mock('instance')] - - instances.expects(:to_pson).returns "foo" - - @pson.render_multiple(instances).should == "foo" - end - - it "should intern by calling 'PSON.parse' on the text and then using from_pson to convert the data into an instance" do - text = "foo" - PSON.expects(:parse).with("foo").returns("type" => "PsonTest", "data" => "foo") - PsonTest.expects(:from_pson).with("foo").returns "parsed_pson" - @pson.intern(PsonTest, text).should == "parsed_pson" - end - - it "should not render twice if 'PSON.parse' creates the appropriate instance" do - text = "foo" - instance = PsonTest.new("foo") - PSON.expects(:parse).with("foo").returns(instance) - PsonTest.expects(:from_pson).never - @pson.intern(PsonTest, text).should equal(instance) - end - - it "should intern by calling 'PSON.parse' on the text and then using from_pson to convert the actual into an instance if the pson has no class/data separation" do - text = "foo" - PSON.expects(:parse).with("foo").returns("foo") - PsonTest.expects(:from_pson).with("foo").returns "parsed_pson" - @pson.intern(PsonTest, text).should == "parsed_pson" - end - - it "should intern multiples by parsing the text and using 'class.intern' on each resulting data structure" do - text = "foo" - PSON.expects(:parse).with("foo").returns ["bar", "baz"] - PsonTest.expects(:from_pson).with("bar").returns "BAR" - PsonTest.expects(:from_pson).with("baz").returns "BAZ" - @pson.intern_multiple(PsonTest, text).should == %w{BAR BAZ} - end - end - end -end diff --git a/spec/unit/network/handler/fileserver_spec.rb b/spec/unit/network/handler/fileserver_spec.rb new file mode 100644 index 000000000..fbb5c8ae2 --- /dev/null +++ b/spec/unit/network/handler/fileserver_spec.rb @@ -0,0 +1,176 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/network/handler/fileserver' + + +describe Puppet::Network::Handler::FileServer do + require 'tmpdir' + + def create_file(filename) + File.open(filename, "w") { |f| f.puts filename} + end + + def create_nested_file() + dirname = File.join(@basedir, "nested_dir") + Dir.mkdir(dirname) + file = File.join(dirname, "nested_dir_file") + create_file(file) + end + + before do + @basedir = File.join(Dir.tmpdir(), "test_network_handler") + Dir.mkdir(@basedir) + @file = File.join(@basedir, "aFile") + @link = File.join(@basedir, "aLink") + create_file(@file) + @mount = Puppet::Network::Handler::FileServer::Mount.new("some_path", @basedir) + end + + it "should list a single directory" do + @mount.list("/", false, false).should == [["/", "directory"]] + end + + it "should list a file within a directory when given the file path" do + @mount.list("/aFile", false, "false").should == [["/", "file"]] + end + + it "should list a file within a directory when given the file path with recursion" do + @mount.list("/aFile", true, "false").should == [["/", "file"]] + end + + it "should return nil for a non-existent path" do + @mount.list("/no_such_file", false, false).should be(nil) + end + + it "should list a symbolic link as a file when given the link path" do + File.symlink(@file, @link) + @mount.list("/aLink", false, false).should == [["/", "file"]] + end + + it "should return nil for a dangling symbolic link when given the link path" do + File.symlink("/some/where", @link) + @mount.list("/aLink", false, false).should be(nil) + end + + it "should list directory contents of a flat directory structure when asked to recurse" do + list = @mount.list("/", true, false) + list.should include(["/aFile", "file"]) + list.should include(["/", "directory"]) + list.should have(2).items + end + + it "should list the contents of a nested directory" do + create_nested_file() + list = @mount.list("/", true, false) + list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , + ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort + end + + it "should list the contents of a directory ignoring files that match" do + create_nested_file() + list = @mount.list("/", true, "*File") + list.sort.should == [ ["/", "directory"] , + ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort + end + + it "should list the contents of a directory ignoring directories that match" do + create_nested_file() + list = @mount.list("/", true, "*nested_dir") + list.sort.should == [ ["/aFile", "file"], ["/", "directory"] ].sort + end + + it "should list the contents of a directory ignoring all ignore patterns that match" do + create_nested_file() + list = @mount.list("/", true, ["*File" , "*nested_dir"]) + list.should == [ ["/", "directory"] ] + end + + it "should list the directory when recursing to a depth of zero" do + create_nested_file() + list = @mount.list("/", 0, false) + list.should == [["/", "directory"]] + end + + it "should list the base directory and files and nested directory to a depth of one" do + create_nested_file() + list = @mount.list("/", 1, false) + list.sort.should == [ ["/aFile", "file"], ["/nested_dir", "directory"], ["/", "directory"] ].sort + end + + it "should list the base directory and files and nested directory to a depth of two" do + create_nested_file() + list = @mount.list("/", 2, false) + list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , + ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort + end + + it "should list the base directory and files and nested directory to a depth greater than the directory structure" do + create_nested_file() + list = @mount.list("/", 42, false) + list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , + ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort + end + + it "should list a valid symbolic link as a file when recursing base dir" do + File.symlink(@file, @link) + list = @mount.list("/", true, false) + list.sort.should == [ ["/", "directory"], ["/aFile", "file"], ["/aLink", "file"] ].sort + end + + it "should not error when a dangling symlink is present" do + File.symlink("/some/where", @link) + lambda { @mount.list("/", true, false) }.should_not raise_error + end + + it "should return the directory contents of valid entries when a dangling symlink is present" do + File.symlink("/some/where", @link) + list = @mount.list("/", true, false) + list.sort.should == [ ["/aFile", "file"], ["/", "directory"] ].sort + end + + describe Puppet::Network::Handler::FileServer::PluginMount do + PLUGINS = Puppet::Network::Handler::FileServer::PLUGINS + + # create a module plugin hierarchy + def create_plugin(mod, plugin) + dirname = File.join(@basedir, mod) + Dir.mkdir(dirname) + plugins = File.join(dirname, PLUGINS) + Dir.mkdir(plugins) + facter = File.join(plugins, plugin) + Dir.mkdir(facter) + create_file(File.join(facter,"fact.rb")) + end + + before :each do + @modules = ["one","two"] + @modules.each { |m| create_plugin(m, "facter") } + + Puppet::Node::Environment.new.stubs(:modulepath).returns @basedir + + @mount = Puppet::Network::Handler::FileServer::PluginMount.new(PLUGINS) + @mount.allow("*") + end + + it "should list a file within a directory when given the file path with recursion" do + @mount.list("facter/fact.rb", true, "false").should == [["/", "file"], ["/", "file"]] + end + + it "should return a merged view of all plugins for all modules" do + list = @mount.list("facter",true,false) + list.should == [["/", "directory"], ["/fact.rb", "file"], ["/", "directory"], ["/fact.rb", "file"]] + end + + it "should not fail for inexistant plugins type" do + lambda { @mount.list("puppet/parser",true,false) }.should_not raise_error + end + + end + + after do + FileUtils.rm_rf(@basedir) + end + +end diff --git a/spec/unit/network/handler/fileserver_spec_spec.rb b/spec/unit/network/handler/fileserver_spec_spec.rb deleted file mode 100644 index fbb5c8ae2..000000000 --- a/spec/unit/network/handler/fileserver_spec_spec.rb +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/network/handler/fileserver' - - -describe Puppet::Network::Handler::FileServer do - require 'tmpdir' - - def create_file(filename) - File.open(filename, "w") { |f| f.puts filename} - end - - def create_nested_file() - dirname = File.join(@basedir, "nested_dir") - Dir.mkdir(dirname) - file = File.join(dirname, "nested_dir_file") - create_file(file) - end - - before do - @basedir = File.join(Dir.tmpdir(), "test_network_handler") - Dir.mkdir(@basedir) - @file = File.join(@basedir, "aFile") - @link = File.join(@basedir, "aLink") - create_file(@file) - @mount = Puppet::Network::Handler::FileServer::Mount.new("some_path", @basedir) - end - - it "should list a single directory" do - @mount.list("/", false, false).should == [["/", "directory"]] - end - - it "should list a file within a directory when given the file path" do - @mount.list("/aFile", false, "false").should == [["/", "file"]] - end - - it "should list a file within a directory when given the file path with recursion" do - @mount.list("/aFile", true, "false").should == [["/", "file"]] - end - - it "should return nil for a non-existent path" do - @mount.list("/no_such_file", false, false).should be(nil) - end - - it "should list a symbolic link as a file when given the link path" do - File.symlink(@file, @link) - @mount.list("/aLink", false, false).should == [["/", "file"]] - end - - it "should return nil for a dangling symbolic link when given the link path" do - File.symlink("/some/where", @link) - @mount.list("/aLink", false, false).should be(nil) - end - - it "should list directory contents of a flat directory structure when asked to recurse" do - list = @mount.list("/", true, false) - list.should include(["/aFile", "file"]) - list.should include(["/", "directory"]) - list.should have(2).items - end - - it "should list the contents of a nested directory" do - create_nested_file() - list = @mount.list("/", true, false) - list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , - ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort - end - - it "should list the contents of a directory ignoring files that match" do - create_nested_file() - list = @mount.list("/", true, "*File") - list.sort.should == [ ["/", "directory"] , - ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort - end - - it "should list the contents of a directory ignoring directories that match" do - create_nested_file() - list = @mount.list("/", true, "*nested_dir") - list.sort.should == [ ["/aFile", "file"], ["/", "directory"] ].sort - end - - it "should list the contents of a directory ignoring all ignore patterns that match" do - create_nested_file() - list = @mount.list("/", true, ["*File" , "*nested_dir"]) - list.should == [ ["/", "directory"] ] - end - - it "should list the directory when recursing to a depth of zero" do - create_nested_file() - list = @mount.list("/", 0, false) - list.should == [["/", "directory"]] - end - - it "should list the base directory and files and nested directory to a depth of one" do - create_nested_file() - list = @mount.list("/", 1, false) - list.sort.should == [ ["/aFile", "file"], ["/nested_dir", "directory"], ["/", "directory"] ].sort - end - - it "should list the base directory and files and nested directory to a depth of two" do - create_nested_file() - list = @mount.list("/", 2, false) - list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , - ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort - end - - it "should list the base directory and files and nested directory to a depth greater than the directory structure" do - create_nested_file() - list = @mount.list("/", 42, false) - list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , - ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort - end - - it "should list a valid symbolic link as a file when recursing base dir" do - File.symlink(@file, @link) - list = @mount.list("/", true, false) - list.sort.should == [ ["/", "directory"], ["/aFile", "file"], ["/aLink", "file"] ].sort - end - - it "should not error when a dangling symlink is present" do - File.symlink("/some/where", @link) - lambda { @mount.list("/", true, false) }.should_not raise_error - end - - it "should return the directory contents of valid entries when a dangling symlink is present" do - File.symlink("/some/where", @link) - list = @mount.list("/", true, false) - list.sort.should == [ ["/aFile", "file"], ["/", "directory"] ].sort - end - - describe Puppet::Network::Handler::FileServer::PluginMount do - PLUGINS = Puppet::Network::Handler::FileServer::PLUGINS - - # create a module plugin hierarchy - def create_plugin(mod, plugin) - dirname = File.join(@basedir, mod) - Dir.mkdir(dirname) - plugins = File.join(dirname, PLUGINS) - Dir.mkdir(plugins) - facter = File.join(plugins, plugin) - Dir.mkdir(facter) - create_file(File.join(facter,"fact.rb")) - end - - before :each do - @modules = ["one","two"] - @modules.each { |m| create_plugin(m, "facter") } - - Puppet::Node::Environment.new.stubs(:modulepath).returns @basedir - - @mount = Puppet::Network::Handler::FileServer::PluginMount.new(PLUGINS) - @mount.allow("*") - end - - it "should list a file within a directory when given the file path with recursion" do - @mount.list("facter/fact.rb", true, "false").should == [["/", "file"], ["/", "file"]] - end - - it "should return a merged view of all plugins for all modules" do - list = @mount.list("facter",true,false) - list.should == [["/", "directory"], ["/fact.rb", "file"], ["/", "directory"], ["/fact.rb", "file"]] - end - - it "should not fail for inexistant plugins type" do - lambda { @mount.list("puppet/parser",true,false) }.should_not raise_error - end - - end - - after do - FileUtils.rm_rf(@basedir) - end - -end diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb new file mode 100644 index 000000000..fc284de82 --- /dev/null +++ b/spec/unit/network/http/api/v1_spec.rb @@ -0,0 +1,122 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/network/http/api/v1' + +class V1RestApiTester + include Puppet::Network::HTTP::API::V1 +end + +describe Puppet::Network::HTTP::API::V1 do + before do + @tester = V1RestApiTester.new + end + + it "should be able to convert a URI into a request" do + @tester.should respond_to(:uri2indirection) + end + + it "should be able to convert a request into a URI" do + @tester.should respond_to(:indirection2uri) + end + + describe "when converting a URI into a request" do + before do + @tester.stubs(:handler).returns "foo" + end + + it "should require the http method, the URI, and the query parameters" do + # Not a terribly useful test, but an important statement for the spec + lambda { @tester.uri2indirection("/foo") }.should raise_error(ArgumentError) + end + + it "should use the first field of the URI as the environment" do + @tester.uri2indirection("GET", "/env/foo/bar", {}).environment.should == Puppet::Node::Environment.new("env") + end + + it "should fail if the environment is not alphanumeric" do + lambda { @tester.uri2indirection("GET", "/env ness/foo/bar", {}) }.should raise_error(ArgumentError) + end + + it "should use the environment from the URI even if one is specified in the parameters" do + @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"}).environment.should == Puppet::Node::Environment.new("env") + end + + it "should use the second field of the URI as the indirection name" do + @tester.uri2indirection("GET", "/env/foo/bar", {}).indirection_name.should == :foo + end + + it "should fail if the indirection name is not alphanumeric" do + lambda { @tester.uri2indirection("GET", "/env/foo ness/bar", {}) }.should raise_error(ArgumentError) + end + + it "should use the remainder of the URI as the indirection key" do + @tester.uri2indirection("GET", "/env/foo/bar", {}).key.should == "bar" + end + + it "should support the indirection key being a /-separated file path" do + @tester.uri2indirection("GET", "/env/foo/bee/baz/bomb", {}).key.should == "bee/baz/bomb" + end + + it "should fail if no indirection key is specified" do + lambda { @tester.uri2indirection("GET", "/env/foo/", {}) }.should raise_error(ArgumentError) + lambda { @tester.uri2indirection("GET", "/env/foo", {}) }.should raise_error(ArgumentError) + end + + it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is singular" do + @tester.uri2indirection("GET", "/env/foo/bar", {}).method.should == :find + end + + it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is plural" do + @tester.uri2indirection("GET", "/env/foos/bar", {}).method.should == :search + end + + it "should choose 'delete' as the indirection method if the http method is a DELETE and the indirection name is singular" do + @tester.uri2indirection("DELETE", "/env/foo/bar", {}).method.should == :destroy + end + + it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is singular" do + @tester.uri2indirection("PUT", "/env/foo/bar", {}).method.should == :save + end + + it "should fail if an indirection method cannot be picked" do + lambda { @tester.uri2indirection("UPDATE", "/env/foo/bar", {}) }.should raise_error(ArgumentError) + end + + it "should URI unescape the indirection key" do + escaped = URI.escape("foo bar") + @tester.uri2indirection("GET", "/env/foo/#{escaped}", {}).key.should == "foo bar" + end + end + + describe "when converting a request into a URI" do + before do + @request = Puppet::Indirector::Request.new(:foo, :find, "with spaces", :foo => :bar, :environment => "myenv") + end + + it "should use the environment as the first field of the URI" do + @tester.indirection2uri(@request).split("/")[1].should == "myenv" + end + + it "should use the indirection as the second field of the URI" do + @tester.indirection2uri(@request).split("/")[2].should == "foo" + end + + it "should pluralize the indirection name if the method is 'search'" do + @request.stubs(:method).returns :search + @tester.indirection2uri(@request).split("/")[2].should == "foos" + end + + it "should use the escaped key as the remainder of the URI" do + escaped = URI.escape("with spaces") + @tester.indirection2uri(@request).split("/")[3].sub(/\?.+/, '').should == escaped + end + + it "should add the query string to the URI" do + @request.expects(:query_string).returns "?query" + @tester.indirection2uri(@request).should =~ /\?query$/ + end + end + +end diff --git a/spec/unit/network/http/api/v1_spec_spec.rb b/spec/unit/network/http/api/v1_spec_spec.rb deleted file mode 100644 index fc284de82..000000000 --- a/spec/unit/network/http/api/v1_spec_spec.rb +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/network/http/api/v1' - -class V1RestApiTester - include Puppet::Network::HTTP::API::V1 -end - -describe Puppet::Network::HTTP::API::V1 do - before do - @tester = V1RestApiTester.new - end - - it "should be able to convert a URI into a request" do - @tester.should respond_to(:uri2indirection) - end - - it "should be able to convert a request into a URI" do - @tester.should respond_to(:indirection2uri) - end - - describe "when converting a URI into a request" do - before do - @tester.stubs(:handler).returns "foo" - end - - it "should require the http method, the URI, and the query parameters" do - # Not a terribly useful test, but an important statement for the spec - lambda { @tester.uri2indirection("/foo") }.should raise_error(ArgumentError) - end - - it "should use the first field of the URI as the environment" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).environment.should == Puppet::Node::Environment.new("env") - end - - it "should fail if the environment is not alphanumeric" do - lambda { @tester.uri2indirection("GET", "/env ness/foo/bar", {}) }.should raise_error(ArgumentError) - end - - it "should use the environment from the URI even if one is specified in the parameters" do - @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"}).environment.should == Puppet::Node::Environment.new("env") - end - - it "should use the second field of the URI as the indirection name" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).indirection_name.should == :foo - end - - it "should fail if the indirection name is not alphanumeric" do - lambda { @tester.uri2indirection("GET", "/env/foo ness/bar", {}) }.should raise_error(ArgumentError) - end - - it "should use the remainder of the URI as the indirection key" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).key.should == "bar" - end - - it "should support the indirection key being a /-separated file path" do - @tester.uri2indirection("GET", "/env/foo/bee/baz/bomb", {}).key.should == "bee/baz/bomb" - end - - it "should fail if no indirection key is specified" do - lambda { @tester.uri2indirection("GET", "/env/foo/", {}) }.should raise_error(ArgumentError) - lambda { @tester.uri2indirection("GET", "/env/foo", {}) }.should raise_error(ArgumentError) - end - - it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is singular" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).method.should == :find - end - - it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is plural" do - @tester.uri2indirection("GET", "/env/foos/bar", {}).method.should == :search - end - - it "should choose 'delete' as the indirection method if the http method is a DELETE and the indirection name is singular" do - @tester.uri2indirection("DELETE", "/env/foo/bar", {}).method.should == :destroy - end - - it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is singular" do - @tester.uri2indirection("PUT", "/env/foo/bar", {}).method.should == :save - end - - it "should fail if an indirection method cannot be picked" do - lambda { @tester.uri2indirection("UPDATE", "/env/foo/bar", {}) }.should raise_error(ArgumentError) - end - - it "should URI unescape the indirection key" do - escaped = URI.escape("foo bar") - @tester.uri2indirection("GET", "/env/foo/#{escaped}", {}).key.should == "foo bar" - end - end - - describe "when converting a request into a URI" do - before do - @request = Puppet::Indirector::Request.new(:foo, :find, "with spaces", :foo => :bar, :environment => "myenv") - end - - it "should use the environment as the first field of the URI" do - @tester.indirection2uri(@request).split("/")[1].should == "myenv" - end - - it "should use the indirection as the second field of the URI" do - @tester.indirection2uri(@request).split("/")[2].should == "foo" - end - - it "should pluralize the indirection name if the method is 'search'" do - @request.stubs(:method).returns :search - @tester.indirection2uri(@request).split("/")[2].should == "foos" - end - - it "should use the escaped key as the remainder of the URI" do - escaped = URI.escape("with spaces") - @tester.indirection2uri(@request).split("/")[3].sub(/\?.+/, '').should == escaped - end - - it "should add the query string to the URI" do - @request.expects(:query_string).returns "?query" - @tester.indirection2uri(@request).should =~ /\?query$/ - end - end - -end diff --git a/spec/unit/network/http/compression_spec.rb b/spec/unit/network/http/compression_spec.rb new file mode 100644 index 000000000..63fd9e715 --- /dev/null +++ b/spec/unit/network/http/compression_spec.rb @@ -0,0 +1,199 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "http compression" do + + describe "when zlib is not available" do + before(:each) do + Puppet.features.stubs(:zlib?).returns false + + require 'puppet/network/http/compression' + class HttpUncompressor + include Puppet::Network::HTTP::Compression::None + end + + @uncompressor = HttpUncompressor.new + end + + it "should have a module function that returns the None underlying module" do + Puppet::Network::HTTP::Compression.module.should == Puppet::Network::HTTP::Compression::None + end + + it "should not add any Accept-Encoding header" do + @uncompressor.add_accept_encoding({}).should == {} + end + + it "should not tamper the body" do + response = stub 'response', :body => "data" + @uncompressor.uncompress_body(response).should == "data" + end + + it "should yield an identity uncompressor" do + response = stub 'response' + @uncompressor.uncompress(response) { |u| + u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) + } + end + end + + describe "when zlib is available" do + confine "Zlib is missing" => Puppet.features.zlib? + + before(:each) do + Puppet.features.stubs(:zlib?).returns true + + require 'puppet/network/http/compression' + class HttpUncompressor + include Puppet::Network::HTTP::Compression::Active + end + + @uncompressor = HttpUncompressor.new + end + + it "should have a module function that returns the Active underlying module" do + Puppet::Network::HTTP::Compression.module.should == Puppet::Network::HTTP::Compression::Active + end + + it "should add an Accept-Encoding header when http compression is available" do + Puppet.settings.expects(:[]).with(:http_compression).returns(true) + headers = @uncompressor.add_accept_encoding({}) + headers.should have_key('accept-encoding') + headers['accept-encoding'].should =~ /gzip/ + headers['accept-encoding'].should =~ /deflate/ + headers['accept-encoding'].should =~ /identity/ + end + + it "should not add Accept-Encoding header if http compression is not available" do + Puppet.settings.stubs(:[]).with(:http_compression).returns(false) + @uncompressor.add_accept_encoding({}).should == {} + end + + describe "when uncompressing response body" do + before do + @response = stub 'response' + @response.stubs(:[]).with('content-encoding') + @response.stubs(:body).returns("mydata") + end + + it "should return untransformed response body with no content-encoding" do + @uncompressor.uncompress_body(@response).should == "mydata" + end + + it "should return untransformed response body with 'identity' content-encoding" do + @response.stubs(:[]).with('content-encoding').returns('identity') + @uncompressor.uncompress_body(@response).should == "mydata" + end + + it "should use a Zlib inflater with 'deflate' content-encoding" do + @response.stubs(:[]).with('content-encoding').returns('deflate') + + inflater = stub 'inflater' + Zlib::Inflate.expects(:new).returns(inflater) + inflater.expects(:inflate).with("mydata").returns "uncompresseddata" + + @uncompressor.uncompress_body(@response).should == "uncompresseddata" + end + + it "should use a GzipReader with 'gzip' content-encoding" do + @response.stubs(:[]).with('content-encoding').returns('gzip') + + io = stub 'io' + StringIO.expects(:new).with("mydata").returns io + + reader = stub 'gzip reader' + Zlib::GzipReader.expects(:new).with(io).returns(reader) + reader.expects(:read).returns "uncompresseddata" + + @uncompressor.uncompress_body(@response).should == "uncompresseddata" + end + end + + describe "when uncompressing by chunk" do + before do + @response = stub 'response' + @response.stubs(:[]).with('content-encoding') + + @inflater = stub_everything 'inflater' + Zlib::Inflate.stubs(:new).returns(@inflater) + end + + it "should yield an identity uncompressor with no content-encoding" do + @uncompressor.uncompress(@response) { |u| + u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) + } + end + + it "should yield an identity uncompressor with 'identity' content-encoding" do + @response.stubs(:[]).with('content-encoding').returns 'identity' + @uncompressor.uncompress(@response) { |u| + u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) + } + end + + %w{gzip deflate}.each do |c| + it "should yield a Zlib uncompressor with '#{c}' content-encoding" do + @response.stubs(:[]).with('content-encoding').returns c + @uncompressor.uncompress(@response) { |u| + u.should be_instance_of(Puppet::Network::HTTP::Compression::Active::ZlibAdapter) + } + end + end + + it "should close the underlying adapter" do + adapter = stub_everything 'adapter' + Puppet::Network::HTTP::Compression::IdentityAdapter.expects(:new).returns(adapter) + + adapter.expects(:close) + @uncompressor.uncompress(@response) { |u| } + end + end + + describe "zlib adapter" do + before do + @inflater = stub_everything 'inflater' + Zlib::Inflate.stubs(:new).returns(@inflater) + @adapter = Puppet::Network::HTTP::Compression::Active::ZlibAdapter.new + end + + it "should initialize the underlying inflater with gzip/zlib header parsing" do + Zlib::Inflate.expects(:new).with(15+32) + Puppet::Network::HTTP::Compression::Active::ZlibAdapter.new + end + + it "should inflate the given chunk" do + @inflater.expects(:inflate).with("chunk") + @adapter.uncompress("chunk") + end + + it "should return the inflated chunk" do + @inflater.stubs(:inflate).with("chunk").returns("uncompressed") + @adapter.uncompress("chunk").should == "uncompressed" + end + + it "should try a 'regular' inflater on Zlib::DataError" do + @inflater.expects(:inflate).raises(Zlib::DataError.new("not a zlib stream")) + inflater = stub_everything 'inflater2' + inflater.expects(:inflate).with("chunk").returns("uncompressed") + Zlib::Inflate.expects(:new).with().returns(inflater) + @adapter.uncompress("chunk") + end + + it "should raise the error the second time" do + @inflater.expects(:inflate).raises(Zlib::DataError.new("not a zlib stream")) + Zlib::Inflate.expects(:new).with().returns(@inflater) + lambda { @adapter.uncompress("chunk") }.should raise_error + end + + it "should finish the stream on close" do + @inflater.expects(:finish) + @adapter.close + end + + it "should close the stream on close" do + @inflater.expects(:close) + @adapter.close + end + end + end +end diff --git a/spec/unit/network/http/compression_spec_spec.rb b/spec/unit/network/http/compression_spec_spec.rb deleted file mode 100644 index 63fd9e715..000000000 --- a/spec/unit/network/http/compression_spec_spec.rb +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "http compression" do - - describe "when zlib is not available" do - before(:each) do - Puppet.features.stubs(:zlib?).returns false - - require 'puppet/network/http/compression' - class HttpUncompressor - include Puppet::Network::HTTP::Compression::None - end - - @uncompressor = HttpUncompressor.new - end - - it "should have a module function that returns the None underlying module" do - Puppet::Network::HTTP::Compression.module.should == Puppet::Network::HTTP::Compression::None - end - - it "should not add any Accept-Encoding header" do - @uncompressor.add_accept_encoding({}).should == {} - end - - it "should not tamper the body" do - response = stub 'response', :body => "data" - @uncompressor.uncompress_body(response).should == "data" - end - - it "should yield an identity uncompressor" do - response = stub 'response' - @uncompressor.uncompress(response) { |u| - u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) - } - end - end - - describe "when zlib is available" do - confine "Zlib is missing" => Puppet.features.zlib? - - before(:each) do - Puppet.features.stubs(:zlib?).returns true - - require 'puppet/network/http/compression' - class HttpUncompressor - include Puppet::Network::HTTP::Compression::Active - end - - @uncompressor = HttpUncompressor.new - end - - it "should have a module function that returns the Active underlying module" do - Puppet::Network::HTTP::Compression.module.should == Puppet::Network::HTTP::Compression::Active - end - - it "should add an Accept-Encoding header when http compression is available" do - Puppet.settings.expects(:[]).with(:http_compression).returns(true) - headers = @uncompressor.add_accept_encoding({}) - headers.should have_key('accept-encoding') - headers['accept-encoding'].should =~ /gzip/ - headers['accept-encoding'].should =~ /deflate/ - headers['accept-encoding'].should =~ /identity/ - end - - it "should not add Accept-Encoding header if http compression is not available" do - Puppet.settings.stubs(:[]).with(:http_compression).returns(false) - @uncompressor.add_accept_encoding({}).should == {} - end - - describe "when uncompressing response body" do - before do - @response = stub 'response' - @response.stubs(:[]).with('content-encoding') - @response.stubs(:body).returns("mydata") - end - - it "should return untransformed response body with no content-encoding" do - @uncompressor.uncompress_body(@response).should == "mydata" - end - - it "should return untransformed response body with 'identity' content-encoding" do - @response.stubs(:[]).with('content-encoding').returns('identity') - @uncompressor.uncompress_body(@response).should == "mydata" - end - - it "should use a Zlib inflater with 'deflate' content-encoding" do - @response.stubs(:[]).with('content-encoding').returns('deflate') - - inflater = stub 'inflater' - Zlib::Inflate.expects(:new).returns(inflater) - inflater.expects(:inflate).with("mydata").returns "uncompresseddata" - - @uncompressor.uncompress_body(@response).should == "uncompresseddata" - end - - it "should use a GzipReader with 'gzip' content-encoding" do - @response.stubs(:[]).with('content-encoding').returns('gzip') - - io = stub 'io' - StringIO.expects(:new).with("mydata").returns io - - reader = stub 'gzip reader' - Zlib::GzipReader.expects(:new).with(io).returns(reader) - reader.expects(:read).returns "uncompresseddata" - - @uncompressor.uncompress_body(@response).should == "uncompresseddata" - end - end - - describe "when uncompressing by chunk" do - before do - @response = stub 'response' - @response.stubs(:[]).with('content-encoding') - - @inflater = stub_everything 'inflater' - Zlib::Inflate.stubs(:new).returns(@inflater) - end - - it "should yield an identity uncompressor with no content-encoding" do - @uncompressor.uncompress(@response) { |u| - u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) - } - end - - it "should yield an identity uncompressor with 'identity' content-encoding" do - @response.stubs(:[]).with('content-encoding').returns 'identity' - @uncompressor.uncompress(@response) { |u| - u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) - } - end - - %w{gzip deflate}.each do |c| - it "should yield a Zlib uncompressor with '#{c}' content-encoding" do - @response.stubs(:[]).with('content-encoding').returns c - @uncompressor.uncompress(@response) { |u| - u.should be_instance_of(Puppet::Network::HTTP::Compression::Active::ZlibAdapter) - } - end - end - - it "should close the underlying adapter" do - adapter = stub_everything 'adapter' - Puppet::Network::HTTP::Compression::IdentityAdapter.expects(:new).returns(adapter) - - adapter.expects(:close) - @uncompressor.uncompress(@response) { |u| } - end - end - - describe "zlib adapter" do - before do - @inflater = stub_everything 'inflater' - Zlib::Inflate.stubs(:new).returns(@inflater) - @adapter = Puppet::Network::HTTP::Compression::Active::ZlibAdapter.new - end - - it "should initialize the underlying inflater with gzip/zlib header parsing" do - Zlib::Inflate.expects(:new).with(15+32) - Puppet::Network::HTTP::Compression::Active::ZlibAdapter.new - end - - it "should inflate the given chunk" do - @inflater.expects(:inflate).with("chunk") - @adapter.uncompress("chunk") - end - - it "should return the inflated chunk" do - @inflater.stubs(:inflate).with("chunk").returns("uncompressed") - @adapter.uncompress("chunk").should == "uncompressed" - end - - it "should try a 'regular' inflater on Zlib::DataError" do - @inflater.expects(:inflate).raises(Zlib::DataError.new("not a zlib stream")) - inflater = stub_everything 'inflater2' - inflater.expects(:inflate).with("chunk").returns("uncompressed") - Zlib::Inflate.expects(:new).with().returns(inflater) - @adapter.uncompress("chunk") - end - - it "should raise the error the second time" do - @inflater.expects(:inflate).raises(Zlib::DataError.new("not a zlib stream")) - Zlib::Inflate.expects(:new).with().returns(@inflater) - lambda { @adapter.uncompress("chunk") }.should raise_error - end - - it "should finish the stream on close" do - @inflater.expects(:finish) - @adapter.close - end - - it "should close the stream on close" do - @inflater.expects(:close) - @adapter.close - end - end - end -end diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb new file mode 100755 index 000000000..e6dd88130 --- /dev/null +++ b/spec/unit/network/http/handler_spec.rb @@ -0,0 +1,455 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/network/http/handler' +require 'puppet/network/rest_authorization' + +class HttpHandled + include Puppet::Network::HTTP::Handler +end + +describe Puppet::Network::HTTP::Handler do + before do + @handler = HttpHandled.new + end + + it "should include the v1 REST API" do + Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::HTTP::API::V1) + end + + it "should include the Rest Authorization system" do + Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::RestAuthorization) + end + + it "should have a method for initializing" do + @handler.should respond_to(:initialize_for_puppet) + end + + describe "when initializing" do + it "should fail when no server type has been provided" do + lambda { @handler.initialize_for_puppet }.should raise_error(ArgumentError) + end + + it "should set server type" do + @handler.initialize_for_puppet("foo") + @handler.server.should == "foo" + end + end + + it "should be able to process requests" do + @handler.should respond_to(:process) + end + + describe "when processing a request" do + before do + @request = stub('http request') + @request.stubs(:[]).returns "foo" + @response = stub('http response') + @model_class = stub('indirected model class') + + @result = stub 'result', :render => "mytext" + + @handler.stubs(:check_authorization) + + stub_server_interface + end + + # Stub out the interface we require our including classes to + # implement. + def stub_server_interface + @handler.stubs(:accept_header ).returns "format_one,format_two" + @handler.stubs(:content_type_header).returns "text/yaml" + @handler.stubs(:set_content_type ).returns "my_result" + @handler.stubs(:set_response ).returns "my_result" + @handler.stubs(:path ).returns "/my_handler/my_result" + @handler.stubs(:http_method ).returns("GET") + @handler.stubs(:params ).returns({}) + @handler.stubs(:content_type ).returns("text/plain") + end + + it "should create an indirection request from the path, parameters, and http method" do + @handler.expects(:path).with(@request).returns "mypath" + @handler.expects(:http_method).with(@request).returns "mymethod" + @handler.expects(:params).with(@request).returns "myparams" + + @handler.expects(:uri2indirection).with("mymethod", "mypath", "myparams").returns stub("request", :method => :find) + + @handler.stubs(:do_find) + + @handler.process(@request, @response) + end + + it "should call the 'do' method associated with the indirection method" do + request = stub 'request' + @handler.expects(:uri2indirection).returns request + + request.expects(:method).returns "mymethod" + + @handler.expects(:do_mymethod).with(request, @request, @response) + + @handler.process(@request, @response) + end + + it "should delegate authorization to the RestAuthorization layer" do + request = stub 'request' + @handler.expects(:uri2indirection).returns request + + request.expects(:method).returns "mymethod" + + @handler.expects(:do_mymethod).with(request, @request, @response) + + @handler.expects(:check_authorization).with(request) + + @handler.process(@request, @response) + end + + it "should return 403 if the request is not authorized" do + request = stub 'request' + @handler.expects(:uri2indirection).returns request + + @handler.expects(:do_mymethod).never + + @handler.expects(:check_authorization).with(request).raises(Puppet::Network::AuthorizationError.new("forbindden")) + + @handler.expects(:set_response).with { |response, body, status| status == 403 } + + @handler.process(@request, @response) + end + + it "should serialize a controller exception when an exception is thrown while finding the model instance" do + @handler.expects(:uri2indirection).returns stub("request", :method => :find) + + @handler.expects(:do_find).raises(ArgumentError, "The exception") + @handler.expects(:set_response).with { |response, body, status| body == "The exception" and status == 400 } + @handler.process(@request, @response) + end + + it "should set the format to text/plain when serializing an exception" do + @handler.expects(:set_content_type).with(@response, "text/plain") + @handler.do_exception(@response, "A test", 404) + end + + it "should raise an error if the request is formatted in an unknown format" do + @handler.stubs(:content_type_header).returns "unknown format" + lambda { @handler.request_format(@request) }.should raise_error + end + + it "should still find the correct format if content type contains charset information" do + @handler.stubs(:content_type_header).returns "text/plain; charset=UTF-8" + @handler.request_format(@request).should == "s" + end + + describe "when finding a model instance" do + before do + @irequest = stub 'indirection_request', :method => :find, :indirection_name => "my_handler", :to_hash => {}, :key => "my_result", :model => @model_class + + @model_class.stubs(:find).returns @result + + @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" + Puppet::Network::FormatHandler.stubs(:format).returns @format + + @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one" + Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat + end + + it "should use the indirection request to find the model class" do + @irequest.expects(:model).returns @model_class + + @handler.do_find(@irequest, @request, @response) + end + + it "should use the escaped request key" do + @model_class.expects(:find).with do |key, args| + key == "my_result" + end.returns @result + @handler.do_find(@irequest, @request, @response) + end + + it "should use a common method for determining the request parameters" do + @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) + @model_class.expects(:find).with do |key, args| + args[:foo] == :baz and args[:bar] == :xyzzy + end.returns @result + @handler.do_find(@irequest, @request, @response) + end + + it "should set the content type to the first format specified in the accept header" do + @handler.expects(:accept_header).with(@request).returns "one,two" + @handler.expects(:set_content_type).with(@response, @oneformat) + @handler.do_find(@irequest, @request, @response) + end + + it "should fail if no accept header is provided" do + @handler.expects(:accept_header).with(@request).returns nil + lambda { @handler.do_find(@irequest, @request, @response) }.should raise_error(ArgumentError) + end + + it "should fail if the accept header does not contain a valid format" do + @handler.expects(:accept_header).with(@request).returns "" + lambda { @handler.do_find(@irequest, @request, @response) }.should raise_error(RuntimeError) + end + + it "should not use an unsuitable format" do + @handler.expects(:accept_header).with(@request).returns "foo,bar" + foo = mock 'foo', :suitable? => false + bar = mock 'bar', :suitable? => true + Puppet::Network::FormatHandler.expects(:format).with("foo").returns foo + Puppet::Network::FormatHandler.expects(:format).with("bar").returns bar + + @handler.expects(:set_content_type).with(@response, bar) # the suitable one + + @handler.do_find(@irequest, @request, @response) + end + + it "should render the result using the first format specified in the accept header" do + + @handler.expects(:accept_header).with(@request).returns "one,two" + @result.expects(:render).with(@oneformat) + + @handler.do_find(@irequest, @request, @response) + end + + it "should use the default status when a model find call succeeds" do + @handler.expects(:set_response).with { |response, body, status| status.nil? } + @handler.do_find(@irequest, @request, @response) + end + + it "should return a serialized object when a model find call succeeds" do + @model_instance = stub('model instance') + @model_instance.expects(:render).returns "my_rendered_object" + + @handler.expects(:set_response).with { |response, body, status| body == "my_rendered_object" } + @model_class.stubs(:find).returns(@model_instance) + @handler.do_find(@irequest, @request, @response) + end + + it "should return a 404 when no model instance can be found" do + @model_class.stubs(:name).returns "my name" + @handler.expects(:set_response).with { |response, body, status| status == 404 } + @model_class.stubs(:find).returns(nil) + @handler.do_find(@irequest, @request, @response) + end + + it "should write a log message when no model instance can be found" do + @model_class.stubs(:name).returns "my name" + @model_class.stubs(:find).returns(nil) + + Puppet.expects(:info).with("Could not find my_handler for 'my_result'") + + @handler.do_find(@irequest, @request, @response) + end + + + it "should serialize the result in with the appropriate format" do + @model_instance = stub('model instance') + + @handler.expects(:format_to_use).returns(@oneformat) + @model_instance.expects(:render).with(@oneformat).returns "my_rendered_object" + @model_class.stubs(:find).returns(@model_instance) + @handler.do_find(@irequest, @request, @response) + end + end + + describe "when searching for model instances" do + before do + @irequest = stub 'indirection_request', :method => :find, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class + + @result1 = mock 'result1' + @result2 = mock 'results' + + @result = [@result1, @result2] + @model_class.stubs(:render_multiple).returns "my rendered instances" + @model_class.stubs(:search).returns(@result) + + @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" + Puppet::Network::FormatHandler.stubs(:format).returns @format + + @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one" + Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat + end + + it "should use the indirection request to find the model" do + @irequest.expects(:model).returns @model_class + + @handler.do_search(@irequest, @request, @response) + end + + it "should use a common method for determining the request parameters" do + @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) + @model_class.expects(:search).with do |key, args| + args[:foo] == :baz and args[:bar] == :xyzzy + end.returns @result + @handler.do_search(@irequest, @request, @response) + end + + it "should use the default status when a model search call succeeds" do + @model_class.stubs(:search).returns(@result) + @handler.do_search(@irequest, @request, @response) + end + + it "should set the content type to the first format returned by the accept header" do + @handler.expects(:accept_header).with(@request).returns "one,two" + @handler.expects(:set_content_type).with(@response, @oneformat) + + @handler.do_search(@irequest, @request, @response) + end + + it "should return a list of serialized objects when a model search call succeeds" do + @handler.expects(:accept_header).with(@request).returns "one,two" + + @model_class.stubs(:search).returns(@result) + + @model_class.expects(:render_multiple).with(@oneformat, @result).returns "my rendered instances" + + @handler.expects(:set_response).with { |response, data| data == "my rendered instances" } + @handler.do_search(@irequest, @request, @response) + end + + it "should return a 404 when searching returns an empty array" do + @model_class.stubs(:name).returns "my name" + @handler.expects(:set_response).with { |response, body, status| status == 404 } + @model_class.stubs(:search).returns([]) + @handler.do_search(@irequest, @request, @response) + end + + it "should return a 404 when searching returns nil" do + @model_class.stubs(:name).returns "my name" + @handler.expects(:set_response).with { |response, body, status| status == 404 } + @model_class.stubs(:search).returns([]) + @handler.do_search(@irequest, @request, @response) + end + end + + describe "when destroying a model instance" do + before do + @irequest = stub 'indirection_request', :method => :destroy, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class + + @result = stub 'result', :render => "the result" + @model_class.stubs(:destroy).returns @result + end + + it "should use the indirection request to find the model" do + @irequest.expects(:model).returns @model_class + + @handler.do_destroy(@irequest, @request, @response) + end + + it "should use the escaped request key to destroy the instance in the model" do + @irequest.expects(:key).returns "foo bar" + @model_class.expects(:destroy).with do |key, args| + key == "foo bar" + end + @handler.do_destroy(@irequest, @request, @response) + end + + it "should use a common method for determining the request parameters" do + @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) + @model_class.expects(:destroy).with do |key, args| + args[:foo] == :baz and args[:bar] == :xyzzy + end + @handler.do_destroy(@irequest, @request, @response) + end + + it "should use the default status code a model destroy call succeeds" do + @handler.expects(:set_response).with { |response, body, status| status.nil? } + @handler.do_destroy(@irequest, @request, @response) + end + + it "should return a yaml-encoded result when a model destroy call succeeds" do + @result = stub 'result', :to_yaml => "the result" + @model_class.expects(:destroy).returns(@result) + + @handler.expects(:set_response).with { |response, body, status| body == "the result" } + + @handler.do_destroy(@irequest, @request, @response) + end + end + + describe "when saving a model instance" do + before do + @irequest = stub 'indirection_request', :method => :save, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class + @handler.stubs(:body).returns('my stuff') + @handler.stubs(:content_type_header).returns("text/yaml") + + @result = stub 'result', :render => "the result" + + @model_instance = stub('indirected model instance', :save => true) + @model_class.stubs(:convert_from).returns(@model_instance) + + @format = stub 'format', :suitable? => true, :name => "format", :mime => "text/format" + Puppet::Network::FormatHandler.stubs(:format).returns @format + @yamlformat = stub 'yaml', :suitable? => true, :name => "yaml", :mime => "text/yaml" + Puppet::Network::FormatHandler.stubs(:format).with("yaml").returns @yamlformat + end + + it "should use the indirection request to find the model" do + @irequest.expects(:model).returns @model_class + + @handler.do_save(@irequest, @request, @response) + end + + it "should use the 'body' hook to retrieve the body of the request" do + @handler.expects(:body).returns "my body" + @model_class.expects(:convert_from).with { |format, body| body == "my body" }.returns @model_instance + + @handler.do_save(@irequest, @request, @response) + end + + it "should fail to save model if data is not specified" do + @handler.stubs(:body).returns('') + + lambda { @handler.do_save(@irequest, @request, @response) }.should raise_error(ArgumentError) + end + + it "should use a common method for determining the request parameters" do + @model_instance.expects(:save).with('key').once + @handler.do_save(@irequest, @request, @response) + end + + it "should use the default status when a model save call succeeds" do + @handler.expects(:set_response).with { |response, body, status| status.nil? } + @handler.do_save(@irequest, @request, @response) + end + + it "should return the yaml-serialized result when a model save call succeeds" do + @model_instance.stubs(:save).returns(@model_instance) + @model_instance.expects(:to_yaml).returns('foo') + @handler.do_save(@irequest, @request, @response) + end + + it "should set the content to yaml" do + @handler.expects(:set_content_type).with(@response, @yamlformat) + @handler.do_save(@irequest, @request, @response) + end + + it "should use the content-type header to know the body format" do + @handler.expects(:content_type_header).returns("text/format") + Puppet::Network::FormatHandler.stubs(:mime).with("text/format").returns @format + + @model_class.expects(:convert_from).with { |format, body| format == "format" }.returns @model_instance + + @handler.do_save(@irequest, @request, @response) + end + end + end + + describe "when resolving node" do + it "should use a look-up from the ip address" do + Resolv.expects(:getname).with("1.2.3.4").returns("host.domain.com") + + @handler.resolve_node(:ip => "1.2.3.4") + end + + it "should return the look-up result" do + Resolv.stubs(:getname).with("1.2.3.4").returns("host.domain.com") + + @handler.resolve_node(:ip => "1.2.3.4").should == "host.domain.com" + end + + it "should return the ip address if resolving fails" do + Resolv.stubs(:getname).with("1.2.3.4").raises(RuntimeError, "no such host") + + @handler.resolve_node(:ip => "1.2.3.4").should == "1.2.3.4" + end + end +end diff --git a/spec/unit/network/http/handler_spec_spec.rb b/spec/unit/network/http/handler_spec_spec.rb deleted file mode 100755 index e6dd88130..000000000 --- a/spec/unit/network/http/handler_spec_spec.rb +++ /dev/null @@ -1,455 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/network/http/handler' -require 'puppet/network/rest_authorization' - -class HttpHandled - include Puppet::Network::HTTP::Handler -end - -describe Puppet::Network::HTTP::Handler do - before do - @handler = HttpHandled.new - end - - it "should include the v1 REST API" do - Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::HTTP::API::V1) - end - - it "should include the Rest Authorization system" do - Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::RestAuthorization) - end - - it "should have a method for initializing" do - @handler.should respond_to(:initialize_for_puppet) - end - - describe "when initializing" do - it "should fail when no server type has been provided" do - lambda { @handler.initialize_for_puppet }.should raise_error(ArgumentError) - end - - it "should set server type" do - @handler.initialize_for_puppet("foo") - @handler.server.should == "foo" - end - end - - it "should be able to process requests" do - @handler.should respond_to(:process) - end - - describe "when processing a request" do - before do - @request = stub('http request') - @request.stubs(:[]).returns "foo" - @response = stub('http response') - @model_class = stub('indirected model class') - - @result = stub 'result', :render => "mytext" - - @handler.stubs(:check_authorization) - - stub_server_interface - end - - # Stub out the interface we require our including classes to - # implement. - def stub_server_interface - @handler.stubs(:accept_header ).returns "format_one,format_two" - @handler.stubs(:content_type_header).returns "text/yaml" - @handler.stubs(:set_content_type ).returns "my_result" - @handler.stubs(:set_response ).returns "my_result" - @handler.stubs(:path ).returns "/my_handler/my_result" - @handler.stubs(:http_method ).returns("GET") - @handler.stubs(:params ).returns({}) - @handler.stubs(:content_type ).returns("text/plain") - end - - it "should create an indirection request from the path, parameters, and http method" do - @handler.expects(:path).with(@request).returns "mypath" - @handler.expects(:http_method).with(@request).returns "mymethod" - @handler.expects(:params).with(@request).returns "myparams" - - @handler.expects(:uri2indirection).with("mymethod", "mypath", "myparams").returns stub("request", :method => :find) - - @handler.stubs(:do_find) - - @handler.process(@request, @response) - end - - it "should call the 'do' method associated with the indirection method" do - request = stub 'request' - @handler.expects(:uri2indirection).returns request - - request.expects(:method).returns "mymethod" - - @handler.expects(:do_mymethod).with(request, @request, @response) - - @handler.process(@request, @response) - end - - it "should delegate authorization to the RestAuthorization layer" do - request = stub 'request' - @handler.expects(:uri2indirection).returns request - - request.expects(:method).returns "mymethod" - - @handler.expects(:do_mymethod).with(request, @request, @response) - - @handler.expects(:check_authorization).with(request) - - @handler.process(@request, @response) - end - - it "should return 403 if the request is not authorized" do - request = stub 'request' - @handler.expects(:uri2indirection).returns request - - @handler.expects(:do_mymethod).never - - @handler.expects(:check_authorization).with(request).raises(Puppet::Network::AuthorizationError.new("forbindden")) - - @handler.expects(:set_response).with { |response, body, status| status == 403 } - - @handler.process(@request, @response) - end - - it "should serialize a controller exception when an exception is thrown while finding the model instance" do - @handler.expects(:uri2indirection).returns stub("request", :method => :find) - - @handler.expects(:do_find).raises(ArgumentError, "The exception") - @handler.expects(:set_response).with { |response, body, status| body == "The exception" and status == 400 } - @handler.process(@request, @response) - end - - it "should set the format to text/plain when serializing an exception" do - @handler.expects(:set_content_type).with(@response, "text/plain") - @handler.do_exception(@response, "A test", 404) - end - - it "should raise an error if the request is formatted in an unknown format" do - @handler.stubs(:content_type_header).returns "unknown format" - lambda { @handler.request_format(@request) }.should raise_error - end - - it "should still find the correct format if content type contains charset information" do - @handler.stubs(:content_type_header).returns "text/plain; charset=UTF-8" - @handler.request_format(@request).should == "s" - end - - describe "when finding a model instance" do - before do - @irequest = stub 'indirection_request', :method => :find, :indirection_name => "my_handler", :to_hash => {}, :key => "my_result", :model => @model_class - - @model_class.stubs(:find).returns @result - - @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" - Puppet::Network::FormatHandler.stubs(:format).returns @format - - @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one" - Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat - end - - it "should use the indirection request to find the model class" do - @irequest.expects(:model).returns @model_class - - @handler.do_find(@irequest, @request, @response) - end - - it "should use the escaped request key" do - @model_class.expects(:find).with do |key, args| - key == "my_result" - end.returns @result - @handler.do_find(@irequest, @request, @response) - end - - it "should use a common method for determining the request parameters" do - @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) - @model_class.expects(:find).with do |key, args| - args[:foo] == :baz and args[:bar] == :xyzzy - end.returns @result - @handler.do_find(@irequest, @request, @response) - end - - it "should set the content type to the first format specified in the accept header" do - @handler.expects(:accept_header).with(@request).returns "one,two" - @handler.expects(:set_content_type).with(@response, @oneformat) - @handler.do_find(@irequest, @request, @response) - end - - it "should fail if no accept header is provided" do - @handler.expects(:accept_header).with(@request).returns nil - lambda { @handler.do_find(@irequest, @request, @response) }.should raise_error(ArgumentError) - end - - it "should fail if the accept header does not contain a valid format" do - @handler.expects(:accept_header).with(@request).returns "" - lambda { @handler.do_find(@irequest, @request, @response) }.should raise_error(RuntimeError) - end - - it "should not use an unsuitable format" do - @handler.expects(:accept_header).with(@request).returns "foo,bar" - foo = mock 'foo', :suitable? => false - bar = mock 'bar', :suitable? => true - Puppet::Network::FormatHandler.expects(:format).with("foo").returns foo - Puppet::Network::FormatHandler.expects(:format).with("bar").returns bar - - @handler.expects(:set_content_type).with(@response, bar) # the suitable one - - @handler.do_find(@irequest, @request, @response) - end - - it "should render the result using the first format specified in the accept header" do - - @handler.expects(:accept_header).with(@request).returns "one,two" - @result.expects(:render).with(@oneformat) - - @handler.do_find(@irequest, @request, @response) - end - - it "should use the default status when a model find call succeeds" do - @handler.expects(:set_response).with { |response, body, status| status.nil? } - @handler.do_find(@irequest, @request, @response) - end - - it "should return a serialized object when a model find call succeeds" do - @model_instance = stub('model instance') - @model_instance.expects(:render).returns "my_rendered_object" - - @handler.expects(:set_response).with { |response, body, status| body == "my_rendered_object" } - @model_class.stubs(:find).returns(@model_instance) - @handler.do_find(@irequest, @request, @response) - end - - it "should return a 404 when no model instance can be found" do - @model_class.stubs(:name).returns "my name" - @handler.expects(:set_response).with { |response, body, status| status == 404 } - @model_class.stubs(:find).returns(nil) - @handler.do_find(@irequest, @request, @response) - end - - it "should write a log message when no model instance can be found" do - @model_class.stubs(:name).returns "my name" - @model_class.stubs(:find).returns(nil) - - Puppet.expects(:info).with("Could not find my_handler for 'my_result'") - - @handler.do_find(@irequest, @request, @response) - end - - - it "should serialize the result in with the appropriate format" do - @model_instance = stub('model instance') - - @handler.expects(:format_to_use).returns(@oneformat) - @model_instance.expects(:render).with(@oneformat).returns "my_rendered_object" - @model_class.stubs(:find).returns(@model_instance) - @handler.do_find(@irequest, @request, @response) - end - end - - describe "when searching for model instances" do - before do - @irequest = stub 'indirection_request', :method => :find, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class - - @result1 = mock 'result1' - @result2 = mock 'results' - - @result = [@result1, @result2] - @model_class.stubs(:render_multiple).returns "my rendered instances" - @model_class.stubs(:search).returns(@result) - - @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" - Puppet::Network::FormatHandler.stubs(:format).returns @format - - @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one" - Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat - end - - it "should use the indirection request to find the model" do - @irequest.expects(:model).returns @model_class - - @handler.do_search(@irequest, @request, @response) - end - - it "should use a common method for determining the request parameters" do - @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) - @model_class.expects(:search).with do |key, args| - args[:foo] == :baz and args[:bar] == :xyzzy - end.returns @result - @handler.do_search(@irequest, @request, @response) - end - - it "should use the default status when a model search call succeeds" do - @model_class.stubs(:search).returns(@result) - @handler.do_search(@irequest, @request, @response) - end - - it "should set the content type to the first format returned by the accept header" do - @handler.expects(:accept_header).with(@request).returns "one,two" - @handler.expects(:set_content_type).with(@response, @oneformat) - - @handler.do_search(@irequest, @request, @response) - end - - it "should return a list of serialized objects when a model search call succeeds" do - @handler.expects(:accept_header).with(@request).returns "one,two" - - @model_class.stubs(:search).returns(@result) - - @model_class.expects(:render_multiple).with(@oneformat, @result).returns "my rendered instances" - - @handler.expects(:set_response).with { |response, data| data == "my rendered instances" } - @handler.do_search(@irequest, @request, @response) - end - - it "should return a 404 when searching returns an empty array" do - @model_class.stubs(:name).returns "my name" - @handler.expects(:set_response).with { |response, body, status| status == 404 } - @model_class.stubs(:search).returns([]) - @handler.do_search(@irequest, @request, @response) - end - - it "should return a 404 when searching returns nil" do - @model_class.stubs(:name).returns "my name" - @handler.expects(:set_response).with { |response, body, status| status == 404 } - @model_class.stubs(:search).returns([]) - @handler.do_search(@irequest, @request, @response) - end - end - - describe "when destroying a model instance" do - before do - @irequest = stub 'indirection_request', :method => :destroy, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class - - @result = stub 'result', :render => "the result" - @model_class.stubs(:destroy).returns @result - end - - it "should use the indirection request to find the model" do - @irequest.expects(:model).returns @model_class - - @handler.do_destroy(@irequest, @request, @response) - end - - it "should use the escaped request key to destroy the instance in the model" do - @irequest.expects(:key).returns "foo bar" - @model_class.expects(:destroy).with do |key, args| - key == "foo bar" - end - @handler.do_destroy(@irequest, @request, @response) - end - - it "should use a common method for determining the request parameters" do - @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) - @model_class.expects(:destroy).with do |key, args| - args[:foo] == :baz and args[:bar] == :xyzzy - end - @handler.do_destroy(@irequest, @request, @response) - end - - it "should use the default status code a model destroy call succeeds" do - @handler.expects(:set_response).with { |response, body, status| status.nil? } - @handler.do_destroy(@irequest, @request, @response) - end - - it "should return a yaml-encoded result when a model destroy call succeeds" do - @result = stub 'result', :to_yaml => "the result" - @model_class.expects(:destroy).returns(@result) - - @handler.expects(:set_response).with { |response, body, status| body == "the result" } - - @handler.do_destroy(@irequest, @request, @response) - end - end - - describe "when saving a model instance" do - before do - @irequest = stub 'indirection_request', :method => :save, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class - @handler.stubs(:body).returns('my stuff') - @handler.stubs(:content_type_header).returns("text/yaml") - - @result = stub 'result', :render => "the result" - - @model_instance = stub('indirected model instance', :save => true) - @model_class.stubs(:convert_from).returns(@model_instance) - - @format = stub 'format', :suitable? => true, :name => "format", :mime => "text/format" - Puppet::Network::FormatHandler.stubs(:format).returns @format - @yamlformat = stub 'yaml', :suitable? => true, :name => "yaml", :mime => "text/yaml" - Puppet::Network::FormatHandler.stubs(:format).with("yaml").returns @yamlformat - end - - it "should use the indirection request to find the model" do - @irequest.expects(:model).returns @model_class - - @handler.do_save(@irequest, @request, @response) - end - - it "should use the 'body' hook to retrieve the body of the request" do - @handler.expects(:body).returns "my body" - @model_class.expects(:convert_from).with { |format, body| body == "my body" }.returns @model_instance - - @handler.do_save(@irequest, @request, @response) - end - - it "should fail to save model if data is not specified" do - @handler.stubs(:body).returns('') - - lambda { @handler.do_save(@irequest, @request, @response) }.should raise_error(ArgumentError) - end - - it "should use a common method for determining the request parameters" do - @model_instance.expects(:save).with('key').once - @handler.do_save(@irequest, @request, @response) - end - - it "should use the default status when a model save call succeeds" do - @handler.expects(:set_response).with { |response, body, status| status.nil? } - @handler.do_save(@irequest, @request, @response) - end - - it "should return the yaml-serialized result when a model save call succeeds" do - @model_instance.stubs(:save).returns(@model_instance) - @model_instance.expects(:to_yaml).returns('foo') - @handler.do_save(@irequest, @request, @response) - end - - it "should set the content to yaml" do - @handler.expects(:set_content_type).with(@response, @yamlformat) - @handler.do_save(@irequest, @request, @response) - end - - it "should use the content-type header to know the body format" do - @handler.expects(:content_type_header).returns("text/format") - Puppet::Network::FormatHandler.stubs(:mime).with("text/format").returns @format - - @model_class.expects(:convert_from).with { |format, body| format == "format" }.returns @model_instance - - @handler.do_save(@irequest, @request, @response) - end - end - end - - describe "when resolving node" do - it "should use a look-up from the ip address" do - Resolv.expects(:getname).with("1.2.3.4").returns("host.domain.com") - - @handler.resolve_node(:ip => "1.2.3.4") - end - - it "should return the look-up result" do - Resolv.stubs(:getname).with("1.2.3.4").returns("host.domain.com") - - @handler.resolve_node(:ip => "1.2.3.4").should == "host.domain.com" - end - - it "should return the ip address if resolving fails" do - Resolv.stubs(:getname).with("1.2.3.4").raises(RuntimeError, "no such host") - - @handler.resolve_node(:ip => "1.2.3.4").should == "1.2.3.4" - end - end -end diff --git a/spec/unit/network/http/mongrel/rest_spec.rb b/spec/unit/network/http/mongrel/rest_spec.rb new file mode 100755 index 000000000..55b6172d3 --- /dev/null +++ b/spec/unit/network/http/mongrel/rest_spec.rb @@ -0,0 +1,249 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../../spec_helper' + +require 'puppet/network/http' + +describe "Puppet::Network::HTTP::MongrelREST" do + confine "Mongrel is not available" => Puppet.features.mongrel? + before do + require 'puppet/network/http/mongrel/rest' + end + + + it "should include the Puppet::Network::HTTP::Handler module" do + Puppet::Network::HTTP::MongrelREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) + end + + describe "when initializing" do + it "should call the Handler's initialization hook with its provided arguments as the server and handler" do + Puppet::Network::HTTP::MongrelREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") + Puppet::Network::HTTP::MongrelREST.new(:server => "my", :handler => "arguments") + end + end + + describe "when receiving a request" do + before do + @params = {} + @request = stub('mongrel http request', :params => @params) + + @head = stub('response head') + @body = stub('response body', :write => true) + @response = stub('mongrel http response') + @response.stubs(:start).yields(@head, @body) + @model_class = stub('indirected model class') + @mongrel = stub('mongrel http server', :register => true) + Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) + @handler = Puppet::Network::HTTP::MongrelREST.new(:server => @mongrel, :handler => :foo) + end + + describe "and using the HTTP Handler interface" do + it "should return the HTTP_ACCEPT parameter as the accept header" do + @params.expects(:[]).with("HTTP_ACCEPT").returns "myaccept" + @handler.accept_header(@request).should == "myaccept" + end + + it "should return the Content-Type parameter as the Content-Type header" do + @params.expects(:[]).with("HTTP_CONTENT_TYPE").returns "mycontent" + @handler.content_type_header(@request).should == "mycontent" + end + + it "should use the REQUEST_METHOD as the http method" do + @params.expects(:[]).with(Mongrel::Const::REQUEST_METHOD).returns "mymethod" + @handler.http_method(@request).should == "mymethod" + end + + it "should return the request path as the path" do + @params.expects(:[]).with(Mongrel::Const::REQUEST_PATH).returns "/foo/bar" + @handler.path(@request).should == "/foo/bar" + end + + it "should return the request body as the body" do + @request.expects(:body).returns StringIO.new("mybody") + @handler.body(@request).should == "mybody" + end + + it "should set the response's content-type header when setting the content type" do + @header = mock 'header' + @response.expects(:header).returns @header + @header.expects(:[]=).with('Content-Type', "mytype") + + @handler.set_content_type(@response, "mytype") + end + + it "should set the status and write the body when setting the response for a successful request" do + head = mock 'head' + body = mock 'body' + @response.expects(:start).with(200).yields(head, body) + + body.expects(:write).with("mybody") + + @handler.set_response(@response, "mybody", 200) + end + + describe "when the result is a File" do + it "should use response send_file" do + head = mock 'head' + body = mock 'body' + stat = stub 'stat', :size => 100 + file = stub 'file', :stat => stat, :path => "/tmp/path" + file.stubs(:is_a?).with(File).returns(true) + + @response.expects(:start).with(200).yields(head, body) + @response.expects(:send_status).with(100) + @response.expects(:send_header) + @response.expects(:send_file).with("/tmp/path") + + @handler.set_response(@response, file, 200) + end + end + + it "should set the status and reason and write the body when setting the response for a successful request" do + head = mock 'head' + body = mock 'body' + @response.expects(:start).with(400, false, "mybody").yields(head, body) + + body.expects(:write).with("mybody") + + @handler.set_response(@response, "mybody", 400) + end + end + + describe "and determining the request parameters" do + before do + @request.stubs(:params).returns({}) + end + + it "should skip empty parameter values" do + @request.expects(:params).returns('QUERY_STRING' => "&=") + lambda { @handler.params(@request) }.should_not raise_error + end + + it "should include the HTTP request parameters, with the keys as symbols" do + @request.expects(:params).returns('QUERY_STRING' => 'foo=baz&bar=xyzzy') + result = @handler.params(@request) + result[:foo].should == "baz" + result[:bar].should == "xyzzy" + end + + it "should CGI-decode the HTTP parameters" do + encoding = CGI.escape("foo bar") + @request.expects(:params).returns('QUERY_STRING' => "foo=#{encoding}") + result = @handler.params(@request) + result[:foo].should == "foo bar" + end + + it "should convert the string 'true' to the boolean" do + @request.expects(:params).returns('QUERY_STRING' => 'foo=true') + result = @handler.params(@request) + result[:foo].should be_true + end + + it "should convert the string 'false' to the boolean" do + @request.expects(:params).returns('QUERY_STRING' => 'foo=false') + result = @handler.params(@request) + result[:foo].should be_false + end + + it "should convert integer arguments to Integers" do + @request.expects(:params).returns('QUERY_STRING' => 'foo=15') + result = @handler.params(@request) + result[:foo].should == 15 + end + + it "should convert floating point arguments to Floats" do + @request.expects(:params).returns('QUERY_STRING' => 'foo=1.5') + result = @handler.params(@request) + result[:foo].should == 1.5 + end + + it "should YAML-load and URI-decode values that are YAML-encoded" do + escaping = CGI.escape(YAML.dump(%w{one two})) + @request.expects(:params).returns('QUERY_STRING' => "foo=#{escaping}") + result = @handler.params(@request) + result[:foo].should == %w{one two} + end + + it "should not allow the client to set the node via the query string" do + @request.stubs(:params).returns('QUERY_STRING' => "node=foo") + @handler.params(@request)[:node].should be_nil + end + + it "should not allow the client to set the IP address via the query string" do + @request.stubs(:params).returns('QUERY_STRING' => "ip=foo") + @handler.params(@request)[:ip].should be_nil + end + + it "should pass the client's ip address to model find" do + @request.stubs(:params).returns("REMOTE_ADDR" => "ipaddress") + @handler.params(@request)[:ip].should == "ipaddress" + end + + it "should pass the client's provided X-Forwared-For value as the ip" do + @request.stubs(:params).returns("HTTP_X_FORWARDED_FOR" => "ipaddress") + @handler.params(@request)[:ip].should == "ipaddress" + end + + it "should pass the client's provided X-Forwared-For first value as the ip" do + @request.stubs(:params).returns("HTTP_X_FORWARDED_FOR" => "ipproxy1,ipproxy2,ipaddress") + @handler.params(@request)[:ip].should == "ipaddress" + end + + it "should pass the client's provided X-Forwared-For value as the ip instead of the REMOTE_ADDR" do + @request.stubs(:params).returns("REMOTE_ADDR" => "remote_addr") + @request.stubs(:params).returns("HTTP_X_FORWARDED_FOR" => "ipaddress") + @handler.params(@request)[:ip].should == "ipaddress" + end + + it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" + @request.stubs(:params).returns("myheader" => "/CN=host.domain.com") + @handler.params(@request) + end + + it "should retrieve the hostname by matching the certificate parameter" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" + @request.stubs(:params).returns("myheader" => "/CN=host.domain.com") + @handler.params(@request)[:node].should == "host.domain.com" + end + + it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" + @request.stubs(:params).returns("myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") + @handler.params(@request) + end + + it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" + @request.stubs(:params).returns("myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") + @handler.params(@request)[:authenticated].should be_true + end + + it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" + @request.stubs(:params).returns("myheader" => "whatever", "certheader" => "/CN=host.domain.com") + @handler.params(@request)[:authenticated].should be_false + end + + it "should consider the host unauthenticated if no certificate information is present" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" + @request.stubs(:params).returns("myheader" => nil, "certheader" => "SUCCESS") + @handler.params(@request)[:authenticated].should be_false + end + + it "should resolve the node name with an ip address look-up if no certificate is present" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" + @request.stubs(:params).returns("myheader" => nil) + @handler.expects(:resolve_node).returns("host.domain.com") + @handler.params(@request)[:node].should == "host.domain.com" + end + end + end +end diff --git a/spec/unit/network/http/mongrel/rest_spec_spec.rb b/spec/unit/network/http/mongrel/rest_spec_spec.rb deleted file mode 100755 index 55b6172d3..000000000 --- a/spec/unit/network/http/mongrel/rest_spec_spec.rb +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../../spec_helper' - -require 'puppet/network/http' - -describe "Puppet::Network::HTTP::MongrelREST" do - confine "Mongrel is not available" => Puppet.features.mongrel? - before do - require 'puppet/network/http/mongrel/rest' - end - - - it "should include the Puppet::Network::HTTP::Handler module" do - Puppet::Network::HTTP::MongrelREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) - end - - describe "when initializing" do - it "should call the Handler's initialization hook with its provided arguments as the server and handler" do - Puppet::Network::HTTP::MongrelREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") - Puppet::Network::HTTP::MongrelREST.new(:server => "my", :handler => "arguments") - end - end - - describe "when receiving a request" do - before do - @params = {} - @request = stub('mongrel http request', :params => @params) - - @head = stub('response head') - @body = stub('response body', :write => true) - @response = stub('mongrel http response') - @response.stubs(:start).yields(@head, @body) - @model_class = stub('indirected model class') - @mongrel = stub('mongrel http server', :register => true) - Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) - @handler = Puppet::Network::HTTP::MongrelREST.new(:server => @mongrel, :handler => :foo) - end - - describe "and using the HTTP Handler interface" do - it "should return the HTTP_ACCEPT parameter as the accept header" do - @params.expects(:[]).with("HTTP_ACCEPT").returns "myaccept" - @handler.accept_header(@request).should == "myaccept" - end - - it "should return the Content-Type parameter as the Content-Type header" do - @params.expects(:[]).with("HTTP_CONTENT_TYPE").returns "mycontent" - @handler.content_type_header(@request).should == "mycontent" - end - - it "should use the REQUEST_METHOD as the http method" do - @params.expects(:[]).with(Mongrel::Const::REQUEST_METHOD).returns "mymethod" - @handler.http_method(@request).should == "mymethod" - end - - it "should return the request path as the path" do - @params.expects(:[]).with(Mongrel::Const::REQUEST_PATH).returns "/foo/bar" - @handler.path(@request).should == "/foo/bar" - end - - it "should return the request body as the body" do - @request.expects(:body).returns StringIO.new("mybody") - @handler.body(@request).should == "mybody" - end - - it "should set the response's content-type header when setting the content type" do - @header = mock 'header' - @response.expects(:header).returns @header - @header.expects(:[]=).with('Content-Type', "mytype") - - @handler.set_content_type(@response, "mytype") - end - - it "should set the status and write the body when setting the response for a successful request" do - head = mock 'head' - body = mock 'body' - @response.expects(:start).with(200).yields(head, body) - - body.expects(:write).with("mybody") - - @handler.set_response(@response, "mybody", 200) - end - - describe "when the result is a File" do - it "should use response send_file" do - head = mock 'head' - body = mock 'body' - stat = stub 'stat', :size => 100 - file = stub 'file', :stat => stat, :path => "/tmp/path" - file.stubs(:is_a?).with(File).returns(true) - - @response.expects(:start).with(200).yields(head, body) - @response.expects(:send_status).with(100) - @response.expects(:send_header) - @response.expects(:send_file).with("/tmp/path") - - @handler.set_response(@response, file, 200) - end - end - - it "should set the status and reason and write the body when setting the response for a successful request" do - head = mock 'head' - body = mock 'body' - @response.expects(:start).with(400, false, "mybody").yields(head, body) - - body.expects(:write).with("mybody") - - @handler.set_response(@response, "mybody", 400) - end - end - - describe "and determining the request parameters" do - before do - @request.stubs(:params).returns({}) - end - - it "should skip empty parameter values" do - @request.expects(:params).returns('QUERY_STRING' => "&=") - lambda { @handler.params(@request) }.should_not raise_error - end - - it "should include the HTTP request parameters, with the keys as symbols" do - @request.expects(:params).returns('QUERY_STRING' => 'foo=baz&bar=xyzzy') - result = @handler.params(@request) - result[:foo].should == "baz" - result[:bar].should == "xyzzy" - end - - it "should CGI-decode the HTTP parameters" do - encoding = CGI.escape("foo bar") - @request.expects(:params).returns('QUERY_STRING' => "foo=#{encoding}") - result = @handler.params(@request) - result[:foo].should == "foo bar" - end - - it "should convert the string 'true' to the boolean" do - @request.expects(:params).returns('QUERY_STRING' => 'foo=true') - result = @handler.params(@request) - result[:foo].should be_true - end - - it "should convert the string 'false' to the boolean" do - @request.expects(:params).returns('QUERY_STRING' => 'foo=false') - result = @handler.params(@request) - result[:foo].should be_false - end - - it "should convert integer arguments to Integers" do - @request.expects(:params).returns('QUERY_STRING' => 'foo=15') - result = @handler.params(@request) - result[:foo].should == 15 - end - - it "should convert floating point arguments to Floats" do - @request.expects(:params).returns('QUERY_STRING' => 'foo=1.5') - result = @handler.params(@request) - result[:foo].should == 1.5 - end - - it "should YAML-load and URI-decode values that are YAML-encoded" do - escaping = CGI.escape(YAML.dump(%w{one two})) - @request.expects(:params).returns('QUERY_STRING' => "foo=#{escaping}") - result = @handler.params(@request) - result[:foo].should == %w{one two} - end - - it "should not allow the client to set the node via the query string" do - @request.stubs(:params).returns('QUERY_STRING' => "node=foo") - @handler.params(@request)[:node].should be_nil - end - - it "should not allow the client to set the IP address via the query string" do - @request.stubs(:params).returns('QUERY_STRING' => "ip=foo") - @handler.params(@request)[:ip].should be_nil - end - - it "should pass the client's ip address to model find" do - @request.stubs(:params).returns("REMOTE_ADDR" => "ipaddress") - @handler.params(@request)[:ip].should == "ipaddress" - end - - it "should pass the client's provided X-Forwared-For value as the ip" do - @request.stubs(:params).returns("HTTP_X_FORWARDED_FOR" => "ipaddress") - @handler.params(@request)[:ip].should == "ipaddress" - end - - it "should pass the client's provided X-Forwared-For first value as the ip" do - @request.stubs(:params).returns("HTTP_X_FORWARDED_FOR" => "ipproxy1,ipproxy2,ipaddress") - @handler.params(@request)[:ip].should == "ipaddress" - end - - it "should pass the client's provided X-Forwared-For value as the ip instead of the REMOTE_ADDR" do - @request.stubs(:params).returns("REMOTE_ADDR" => "remote_addr") - @request.stubs(:params).returns("HTTP_X_FORWARDED_FOR" => "ipaddress") - @handler.params(@request)[:ip].should == "ipaddress" - end - - it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" - @request.stubs(:params).returns("myheader" => "/CN=host.domain.com") - @handler.params(@request) - end - - it "should retrieve the hostname by matching the certificate parameter" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" - @request.stubs(:params).returns("myheader" => "/CN=host.domain.com") - @handler.params(@request)[:node].should == "host.domain.com" - end - - it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" - @request.stubs(:params).returns("myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") - @handler.params(@request) - end - - it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" - @request.stubs(:params).returns("myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") - @handler.params(@request)[:authenticated].should be_true - end - - it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" - @request.stubs(:params).returns("myheader" => "whatever", "certheader" => "/CN=host.domain.com") - @handler.params(@request)[:authenticated].should be_false - end - - it "should consider the host unauthenticated if no certificate information is present" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" - @request.stubs(:params).returns("myheader" => nil, "certheader" => "SUCCESS") - @handler.params(@request)[:authenticated].should be_false - end - - it "should resolve the node name with an ip address look-up if no certificate is present" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" - @request.stubs(:params).returns("myheader" => nil) - @handler.expects(:resolve_node).returns("host.domain.com") - @handler.params(@request)[:node].should == "host.domain.com" - end - end - end -end diff --git a/spec/unit/network/http/mongrel/xmlrpc_spec.rb b/spec/unit/network/http/mongrel/xmlrpc_spec.rb new file mode 100755 index 000000000..e69de29bb diff --git a/spec/unit/network/http/mongrel/xmlrpc_spec_spec.rb b/spec/unit/network/http/mongrel/xmlrpc_spec_spec.rb deleted file mode 100755 index e69de29bb..000000000 diff --git a/spec/unit/network/http/mongrel_spec.rb b/spec/unit/network/http/mongrel_spec.rb new file mode 100755 index 000000000..c0ca68e43 --- /dev/null +++ b/spec/unit/network/http/mongrel_spec.rb @@ -0,0 +1,131 @@ +#!/usr/bin/env ruby +# +# Created by Rick Bradley on 2007-10-15. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/network/http' + +describe "Puppet::Network::HTTP::Mongrel", "after initializing" do + confine "Mongrel is not available" => Puppet.features.mongrel? + + it "should not be listening" do + require 'puppet/network/http/mongrel' + + Puppet::Network::HTTP::Mongrel.new.should_not be_listening + end +end + +describe "Puppet::Network::HTTP::Mongrel", "when turning on listening" do + confine "Mongrel is not available" => Puppet.features.mongrel? + + before do + require 'puppet/network/http/mongrel' + + @server = Puppet::Network::HTTP::Mongrel.new + @mock_mongrel = mock('mongrel') + @mock_mongrel.stubs(:run) + @mock_mongrel.stubs(:register) + Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) + + @mock_puppet_mongrel = mock('puppet_mongrel') + Puppet::Network::HTTPServer::Mongrel.stubs(:new).returns(@mock_puppet_mongrel) + + @listen_params = { :address => "127.0.0.1", :port => 31337, :protocols => [ :rest, :xmlrpc ], :xmlrpc_handlers => [ :status, :fileserver ] } + end + + it "should fail if already listening" do + @server.listen(@listen_params) + Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError) + end + + it "should require at least one protocol" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :protocols == k}) }.should raise_error(ArgumentError) + end + + it "should require a listening address to be specified" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) + end + + it "should require a listening port to be specified" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError) + end + + it "should order a mongrel server to start" do + @mock_mongrel.expects(:run) + @server.listen(@listen_params) + end + + it "should tell mongrel to listen on the specified address and port" do + Mongrel::HttpServer.expects(:new).with("127.0.0.1", 31337).returns(@mock_mongrel) + @server.listen(@listen_params) + end + + it "should be listening" do + Mongrel::HttpServer.expects(:new).returns(@mock_mongrel) + @server.listen(@listen_params) + @server.should be_listening + end + + describe "when providing REST services" do + it "should instantiate a handler at / for handling REST calls" do + Puppet::Network::HTTP::MongrelREST.expects(:new).returns "myhandler" + @mock_mongrel.expects(:register).with("/", "myhandler") + + @server.listen(@listen_params) + end + + it "should use a Mongrel + REST class to configure Mongrel when REST services are requested" do + @server.expects(:class_for_protocol).with(:rest).at_least_once.returns(Puppet::Network::HTTP::MongrelREST) + @server.listen(@listen_params) + end + end + + describe "when providing XMLRPC services" do + it "should do nothing if no xmlrpc handlers have been provided" do + Puppet::Network::HTTPServer::Mongrel.expects(:new).never + @server.listen(@listen_params.merge(:xmlrpc_handlers => [])) + end + + it "should create an instance of the existing Mongrel http server with the right handlers" do + Puppet::Network::HTTPServer::Mongrel.expects(:new).with([:status, :master]).returns(@mock_puppet_mongrel) + @server.listen(@listen_params.merge(:xmlrpc_handlers => [:status, :master])) + end + + it "should register the Mongrel server instance at /RPC2" do + @mock_mongrel.expects(:register).with("/RPC2", @mock_puppet_mongrel) + + @server.listen(@listen_params.merge(:xmlrpc_handlers => [:status, :master])) + end + end +end + +describe "Puppet::Network::HTTP::Mongrel", "when turning off listening" do + confine "Mongrel is not available" => Puppet.features.mongrel? + + before do + @mock_mongrel = mock('mongrel httpserver') + @mock_mongrel.stubs(:run) + @mock_mongrel.stubs(:register) + Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) + @server = Puppet::Network::HTTP::Mongrel.new + @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] } + end + + it "should fail unless listening" do + Proc.new { @server.unlisten }.should raise_error(RuntimeError) + end + + it "should order mongrel server to stop" do + @server.listen(@listen_params) + @mock_mongrel.expects(:stop) + @server.unlisten + end + + it "should not be listening" do + @server.listen(@listen_params) + @mock_mongrel.stubs(:stop) + @server.unlisten + @server.should_not be_listening + end +end diff --git a/spec/unit/network/http/mongrel_spec_spec.rb b/spec/unit/network/http/mongrel_spec_spec.rb deleted file mode 100755 index c0ca68e43..000000000 --- a/spec/unit/network/http/mongrel_spec_spec.rb +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Rick Bradley on 2007-10-15. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/network/http' - -describe "Puppet::Network::HTTP::Mongrel", "after initializing" do - confine "Mongrel is not available" => Puppet.features.mongrel? - - it "should not be listening" do - require 'puppet/network/http/mongrel' - - Puppet::Network::HTTP::Mongrel.new.should_not be_listening - end -end - -describe "Puppet::Network::HTTP::Mongrel", "when turning on listening" do - confine "Mongrel is not available" => Puppet.features.mongrel? - - before do - require 'puppet/network/http/mongrel' - - @server = Puppet::Network::HTTP::Mongrel.new - @mock_mongrel = mock('mongrel') - @mock_mongrel.stubs(:run) - @mock_mongrel.stubs(:register) - Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) - - @mock_puppet_mongrel = mock('puppet_mongrel') - Puppet::Network::HTTPServer::Mongrel.stubs(:new).returns(@mock_puppet_mongrel) - - @listen_params = { :address => "127.0.0.1", :port => 31337, :protocols => [ :rest, :xmlrpc ], :xmlrpc_handlers => [ :status, :fileserver ] } - end - - it "should fail if already listening" do - @server.listen(@listen_params) - Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError) - end - - it "should require at least one protocol" do - Proc.new { @server.listen(@listen_params.delete_if {|k,v| :protocols == k}) }.should raise_error(ArgumentError) - end - - it "should require a listening address to be specified" do - Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) - end - - it "should require a listening port to be specified" do - Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError) - end - - it "should order a mongrel server to start" do - @mock_mongrel.expects(:run) - @server.listen(@listen_params) - end - - it "should tell mongrel to listen on the specified address and port" do - Mongrel::HttpServer.expects(:new).with("127.0.0.1", 31337).returns(@mock_mongrel) - @server.listen(@listen_params) - end - - it "should be listening" do - Mongrel::HttpServer.expects(:new).returns(@mock_mongrel) - @server.listen(@listen_params) - @server.should be_listening - end - - describe "when providing REST services" do - it "should instantiate a handler at / for handling REST calls" do - Puppet::Network::HTTP::MongrelREST.expects(:new).returns "myhandler" - @mock_mongrel.expects(:register).with("/", "myhandler") - - @server.listen(@listen_params) - end - - it "should use a Mongrel + REST class to configure Mongrel when REST services are requested" do - @server.expects(:class_for_protocol).with(:rest).at_least_once.returns(Puppet::Network::HTTP::MongrelREST) - @server.listen(@listen_params) - end - end - - describe "when providing XMLRPC services" do - it "should do nothing if no xmlrpc handlers have been provided" do - Puppet::Network::HTTPServer::Mongrel.expects(:new).never - @server.listen(@listen_params.merge(:xmlrpc_handlers => [])) - end - - it "should create an instance of the existing Mongrel http server with the right handlers" do - Puppet::Network::HTTPServer::Mongrel.expects(:new).with([:status, :master]).returns(@mock_puppet_mongrel) - @server.listen(@listen_params.merge(:xmlrpc_handlers => [:status, :master])) - end - - it "should register the Mongrel server instance at /RPC2" do - @mock_mongrel.expects(:register).with("/RPC2", @mock_puppet_mongrel) - - @server.listen(@listen_params.merge(:xmlrpc_handlers => [:status, :master])) - end - end -end - -describe "Puppet::Network::HTTP::Mongrel", "when turning off listening" do - confine "Mongrel is not available" => Puppet.features.mongrel? - - before do - @mock_mongrel = mock('mongrel httpserver') - @mock_mongrel.stubs(:run) - @mock_mongrel.stubs(:register) - Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) - @server = Puppet::Network::HTTP::Mongrel.new - @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] } - end - - it "should fail unless listening" do - Proc.new { @server.unlisten }.should raise_error(RuntimeError) - end - - it "should order mongrel server to stop" do - @server.listen(@listen_params) - @mock_mongrel.expects(:stop) - @server.unlisten - end - - it "should not be listening" do - @server.listen(@listen_params) - @mock_mongrel.stubs(:stop) - @server.unlisten - @server.should_not be_listening - end -end diff --git a/spec/unit/network/http/rack/rest_spec.rb b/spec/unit/network/http/rack/rest_spec.rb new file mode 100755 index 000000000..b9d835284 --- /dev/null +++ b/spec/unit/network/http/rack/rest_spec.rb @@ -0,0 +1,249 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../../spec_helper' +require 'puppet/network/http/rack' if Puppet.features.rack? +require 'puppet/network/http/rack/rest' + +describe "Puppet::Network::HTTP::RackREST" do + confine "Rack is not available" => Puppet.features.rack? + + it "should include the Puppet::Network::HTTP::Handler module" do + Puppet::Network::HTTP::RackREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) + end + + describe "when initializing" do + it "should call the Handler's initialization hook with its provided arguments" do + Puppet::Network::HTTP::RackREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") + Puppet::Network::HTTP::RackREST.new(:server => "my", :handler => "arguments") + end + end + + describe "when serving a request" do + before :all do + @model_class = stub('indirected model class') + Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) + @handler = Puppet::Network::HTTP::RackREST.new(:handler => :foo) + end + + before :each do + @response = Rack::Response.new() + end + + def mk_req(uri, opts = {}) + env = Rack::MockRequest.env_for(uri, opts) + Rack::Request.new(env) + end + + describe "and using the HTTP Handler interface" do + it "should return the HTTP_ACCEPT parameter as the accept header" do + req = mk_req('/', 'HTTP_ACCEPT' => 'myaccept') + @handler.accept_header(req).should == "myaccept" + end + + it "should return the CONTENT_TYPE parameter as the content type header" do + req = mk_req('/', 'CONTENT_TYPE' => 'mycontent') + @handler.content_type_header(req).should == "mycontent" + end + + it "should use the REQUEST_METHOD as the http method" do + req = mk_req('/', :method => 'MYMETHOD') + @handler.http_method(req).should == "MYMETHOD" + end + + it "should return the request path as the path" do + req = mk_req('/foo/bar') + @handler.path(req).should == "/foo/bar" + end + + it "should return the request body as the body" do + req = mk_req('/foo/bar', :input => 'mybody') + @handler.body(req).should == "mybody" + end + + it "should set the response's content-type header when setting the content type" do + @header = mock 'header' + @response.expects(:header).returns @header + @header.expects(:[]=).with('Content-Type', "mytype") + + @handler.set_content_type(@response, "mytype") + end + + it "should set the status and write the body when setting the response for a request" do + @response.expects(:status=).with(400) + @response.expects(:write).with("mybody") + + @handler.set_response(@response, "mybody", 400) + end + + describe "when result is a File" do + before :each do + stat = stub 'stat', :size => 100 + @file = stub 'file', :stat => stat, :path => "/tmp/path" + @file.stubs(:is_a?).with(File).returns(true) + end + + it "should set the Content-Length header" do + @response.expects(:[]=).with("Content-Length", 100) + + @handler.set_response(@response, @file, 200) + end + + it "should return a RackFile adapter as body" do + @response.expects(:body=).with { |val| val.is_a?(Puppet::Network::HTTP::RackREST::RackFile) } + + @handler.set_response(@response, @file, 200) + end + end + end + + describe "and determining the request parameters" do + it "should include the HTTP request parameters, with the keys as symbols" do + req = mk_req('/?foo=baz&bar=xyzzy') + result = @handler.params(req) + result[:foo].should == "baz" + result[:bar].should == "xyzzy" + end + + it "should CGI-decode the HTTP parameters" do + encoding = CGI.escape("foo bar") + req = mk_req("/?foo=#{encoding}") + result = @handler.params(req) + result[:foo].should == "foo bar" + end + + it "should convert the string 'true' to the boolean" do + req = mk_req("/?foo=true") + result = @handler.params(req) + result[:foo].should be_true + end + + it "should convert the string 'false' to the boolean" do + req = mk_req("/?foo=false") + result = @handler.params(req) + result[:foo].should be_false + end + + it "should convert integer arguments to Integers" do + req = mk_req("/?foo=15") + result = @handler.params(req) + result[:foo].should == 15 + end + + it "should convert floating point arguments to Floats" do + req = mk_req("/?foo=1.5") + result = @handler.params(req) + result[:foo].should == 1.5 + end + + it "should YAML-load and CGI-decode values that are YAML-encoded" do + escaping = CGI.escape(YAML.dump(%w{one two})) + req = mk_req("/?foo=#{escaping}") + result = @handler.params(req) + result[:foo].should == %w{one two} + end + + it "should not allow the client to set the node via the query string" do + req = mk_req("/?node=foo") + @handler.params(req)[:node].should be_nil + end + + it "should not allow the client to set the IP address via the query string" do + req = mk_req("/?ip=foo") + @handler.params(req)[:ip].should be_nil + end + + it "should pass the client's ip address to model find" do + req = mk_req("/", 'REMOTE_ADDR' => 'ipaddress') + @handler.params(req)[:ip].should == "ipaddress" + end + + it "should set 'authenticated' to false if no certificate is present" do + req = mk_req('/') + @handler.params(req)[:authenticated].should be_false + end + end + + describe "with pre-validated certificates" do + + it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" + req = mk_req('/', "myheader" => "/CN=host.domain.com") + @handler.params(req) + end + + it "should retrieve the hostname by matching the certificate parameter" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" + req = mk_req('/', "myheader" => "/CN=host.domain.com") + @handler.params(req)[:node].should == "host.domain.com" + end + + it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" + req = mk_req('/', "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") + @handler.params(req) + end + + it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" + req = mk_req('/', "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") + @handler.params(req)[:authenticated].should be_true + end + + it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" + req = mk_req('/', "myheader" => "whatever", "certheader" => "/CN=host.domain.com") + @handler.params(req)[:authenticated].should be_false + end + + it "should consider the host unauthenticated if no certificate information is present" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" + req = mk_req('/', "myheader" => nil, "certheader" => "/CN=host.domain.com") + @handler.params(req)[:authenticated].should be_false + end + + it "should resolve the node name with an ip address look-up if no certificate is present" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" + req = mk_req('/', "myheader" => nil) + @handler.expects(:resolve_node).returns("host.domain.com") + @handler.params(req)[:node].should == "host.domain.com" + end + end + end +end + +describe Puppet::Network::HTTP::RackREST::RackFile do + before(:each) do + stat = stub 'stat', :size => 100 + @file = stub 'file', :stat => stat, :path => "/tmp/path" + @rackfile = Puppet::Network::HTTP::RackREST::RackFile.new(@file) + end + + it "should have an each method" do + @rackfile.should be_respond_to(:each) + end + + it "should yield file chunks by chunks" do + @file.expects(:read).times(3).with(8192).returns("1", "2", nil) + i = 1 + @rackfile.each do |chunk| + chunk.to_i.should == i + i += 1 + end + end + + it "should have a close method" do + @rackfile.should be_respond_to(:close) + end + + it "should delegate close to File close" do + @file.expects(:close) + @rackfile.close + end +end \ No newline at end of file diff --git a/spec/unit/network/http/rack/rest_spec_spec.rb b/spec/unit/network/http/rack/rest_spec_spec.rb deleted file mode 100755 index b9d835284..000000000 --- a/spec/unit/network/http/rack/rest_spec_spec.rb +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../../spec_helper' -require 'puppet/network/http/rack' if Puppet.features.rack? -require 'puppet/network/http/rack/rest' - -describe "Puppet::Network::HTTP::RackREST" do - confine "Rack is not available" => Puppet.features.rack? - - it "should include the Puppet::Network::HTTP::Handler module" do - Puppet::Network::HTTP::RackREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) - end - - describe "when initializing" do - it "should call the Handler's initialization hook with its provided arguments" do - Puppet::Network::HTTP::RackREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") - Puppet::Network::HTTP::RackREST.new(:server => "my", :handler => "arguments") - end - end - - describe "when serving a request" do - before :all do - @model_class = stub('indirected model class') - Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) - @handler = Puppet::Network::HTTP::RackREST.new(:handler => :foo) - end - - before :each do - @response = Rack::Response.new() - end - - def mk_req(uri, opts = {}) - env = Rack::MockRequest.env_for(uri, opts) - Rack::Request.new(env) - end - - describe "and using the HTTP Handler interface" do - it "should return the HTTP_ACCEPT parameter as the accept header" do - req = mk_req('/', 'HTTP_ACCEPT' => 'myaccept') - @handler.accept_header(req).should == "myaccept" - end - - it "should return the CONTENT_TYPE parameter as the content type header" do - req = mk_req('/', 'CONTENT_TYPE' => 'mycontent') - @handler.content_type_header(req).should == "mycontent" - end - - it "should use the REQUEST_METHOD as the http method" do - req = mk_req('/', :method => 'MYMETHOD') - @handler.http_method(req).should == "MYMETHOD" - end - - it "should return the request path as the path" do - req = mk_req('/foo/bar') - @handler.path(req).should == "/foo/bar" - end - - it "should return the request body as the body" do - req = mk_req('/foo/bar', :input => 'mybody') - @handler.body(req).should == "mybody" - end - - it "should set the response's content-type header when setting the content type" do - @header = mock 'header' - @response.expects(:header).returns @header - @header.expects(:[]=).with('Content-Type', "mytype") - - @handler.set_content_type(@response, "mytype") - end - - it "should set the status and write the body when setting the response for a request" do - @response.expects(:status=).with(400) - @response.expects(:write).with("mybody") - - @handler.set_response(@response, "mybody", 400) - end - - describe "when result is a File" do - before :each do - stat = stub 'stat', :size => 100 - @file = stub 'file', :stat => stat, :path => "/tmp/path" - @file.stubs(:is_a?).with(File).returns(true) - end - - it "should set the Content-Length header" do - @response.expects(:[]=).with("Content-Length", 100) - - @handler.set_response(@response, @file, 200) - end - - it "should return a RackFile adapter as body" do - @response.expects(:body=).with { |val| val.is_a?(Puppet::Network::HTTP::RackREST::RackFile) } - - @handler.set_response(@response, @file, 200) - end - end - end - - describe "and determining the request parameters" do - it "should include the HTTP request parameters, with the keys as symbols" do - req = mk_req('/?foo=baz&bar=xyzzy') - result = @handler.params(req) - result[:foo].should == "baz" - result[:bar].should == "xyzzy" - end - - it "should CGI-decode the HTTP parameters" do - encoding = CGI.escape("foo bar") - req = mk_req("/?foo=#{encoding}") - result = @handler.params(req) - result[:foo].should == "foo bar" - end - - it "should convert the string 'true' to the boolean" do - req = mk_req("/?foo=true") - result = @handler.params(req) - result[:foo].should be_true - end - - it "should convert the string 'false' to the boolean" do - req = mk_req("/?foo=false") - result = @handler.params(req) - result[:foo].should be_false - end - - it "should convert integer arguments to Integers" do - req = mk_req("/?foo=15") - result = @handler.params(req) - result[:foo].should == 15 - end - - it "should convert floating point arguments to Floats" do - req = mk_req("/?foo=1.5") - result = @handler.params(req) - result[:foo].should == 1.5 - end - - it "should YAML-load and CGI-decode values that are YAML-encoded" do - escaping = CGI.escape(YAML.dump(%w{one two})) - req = mk_req("/?foo=#{escaping}") - result = @handler.params(req) - result[:foo].should == %w{one two} - end - - it "should not allow the client to set the node via the query string" do - req = mk_req("/?node=foo") - @handler.params(req)[:node].should be_nil - end - - it "should not allow the client to set the IP address via the query string" do - req = mk_req("/?ip=foo") - @handler.params(req)[:ip].should be_nil - end - - it "should pass the client's ip address to model find" do - req = mk_req("/", 'REMOTE_ADDR' => 'ipaddress') - @handler.params(req)[:ip].should == "ipaddress" - end - - it "should set 'authenticated' to false if no certificate is present" do - req = mk_req('/') - @handler.params(req)[:authenticated].should be_false - end - end - - describe "with pre-validated certificates" do - - it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" - req = mk_req('/', "myheader" => "/CN=host.domain.com") - @handler.params(req) - end - - it "should retrieve the hostname by matching the certificate parameter" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" - req = mk_req('/', "myheader" => "/CN=host.domain.com") - @handler.params(req)[:node].should == "host.domain.com" - end - - it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" - req = mk_req('/', "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") - @handler.params(req) - end - - it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" - req = mk_req('/', "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") - @handler.params(req)[:authenticated].should be_true - end - - it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" - req = mk_req('/', "myheader" => "whatever", "certheader" => "/CN=host.domain.com") - @handler.params(req)[:authenticated].should be_false - end - - it "should consider the host unauthenticated if no certificate information is present" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" - req = mk_req('/', "myheader" => nil, "certheader" => "/CN=host.domain.com") - @handler.params(req)[:authenticated].should be_false - end - - it "should resolve the node name with an ip address look-up if no certificate is present" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" - req = mk_req('/', "myheader" => nil) - @handler.expects(:resolve_node).returns("host.domain.com") - @handler.params(req)[:node].should == "host.domain.com" - end - end - end -end - -describe Puppet::Network::HTTP::RackREST::RackFile do - before(:each) do - stat = stub 'stat', :size => 100 - @file = stub 'file', :stat => stat, :path => "/tmp/path" - @rackfile = Puppet::Network::HTTP::RackREST::RackFile.new(@file) - end - - it "should have an each method" do - @rackfile.should be_respond_to(:each) - end - - it "should yield file chunks by chunks" do - @file.expects(:read).times(3).with(8192).returns("1", "2", nil) - i = 1 - @rackfile.each do |chunk| - chunk.to_i.should == i - i += 1 - end - end - - it "should have a close method" do - @rackfile.should be_respond_to(:close) - end - - it "should delegate close to File close" do - @file.expects(:close) - @rackfile.close - end -end \ No newline at end of file diff --git a/spec/unit/network/http/rack/xmlrpc_spec.rb b/spec/unit/network/http/rack/xmlrpc_spec.rb new file mode 100755 index 000000000..abfe84ba6 --- /dev/null +++ b/spec/unit/network/http/rack/xmlrpc_spec.rb @@ -0,0 +1,157 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../../spec_helper' +require 'puppet/network/http/rack' if Puppet.features.rack? +require 'puppet/network/http/rack/xmlrpc' if Puppet.features.rack? + +describe "Puppet::Network::HTTP::RackXMLRPC" do + confine "Rack is not available" => Puppet.features.rack? + + describe "when initializing" do + it "should create an Puppet::Network::XMLRPCServer" do + Puppet::Network::XMLRPCServer.expects(:new).returns stub_everything + Puppet::Network::HTTP::RackXMLRPC.new([]) + end + + it "should create each handler" do + handler = stub_everything 'handler' + Puppet::Network::XMLRPCServer.any_instance.stubs(:add_handler) + Puppet::Network::Handler.expects(:handler).returns(handler).times(2) + Puppet::Network::HTTP::RackXMLRPC.new([:foo, :bar]) + end + + it "should add each handler to the XMLRPCserver" do + handler = stub_everything 'handler' + Puppet::Network::Handler.stubs(:handler).returns(handler) + Puppet::Network::XMLRPCServer.any_instance.expects(:add_handler).times(2) + Puppet::Network::HTTP::RackXMLRPC.new([:foo, :bar]) + end + end + + describe "when serving a request" do + + before :each do + foo_handler = stub_everything 'foo_handler' + Puppet::Network::Handler.stubs(:handler).with(:foo).returns foo_handler + Puppet::Network::XMLRPCServer.any_instance.stubs(:add_handler) + Puppet::Network::XMLRPCServer.any_instance.stubs(:process).returns('') + @handler = Puppet::Network::HTTP::RackXMLRPC.new([:foo]) + end + + before :each do + @response = Rack::Response.new() + end + + def mk_req(opts = {}) + opts[:method] = 'POST' if !opts[:method] + opts['CONTENT_TYPE'] = 'text/xml; foo=bar' if !opts['CONTENT_TYPE'] + env = Rack::MockRequest.env_for('/RPC2', opts) + Rack::Request.new(env) + end + + it "should reject non-POST requests" do + req = mk_req :method => 'PUT' + @handler.process(req, @response) + @response.status.should == 405 + end + + it "should reject non text/xml requests" do + req = mk_req 'CONTENT_TYPE' => 'yadda/plain' + end + + it "should create a ClientRequest" do + cr = Puppet::Network::ClientRequest.new(nil, '127.0.0.1', false) + Puppet::Network::ClientRequest.expects(:new).returns cr + req = mk_req + @handler.process(req, @response) + end + + it "should let xmlrpcserver process the request" do + Puppet::Network::XMLRPCServer.any_instance.expects(:process).returns('yay') + req = mk_req + @handler.process(req, @response) + end + + it "should report the response as OK" do + req = mk_req + @handler.process(req, @response) + @response.status.should == 200 + end + + it "should report the response with the correct content type" do + req = mk_req + @handler.process(req, @response) + @response['Content-Type'].should == 'text/xml; charset=utf-8' + end + + it "should set 'authenticated' to false if no certificate is present" do + req = mk_req + Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| authenticated == false } + @handler.process(req, @response) + end + + it "should use the client's ip address" do + req = mk_req 'REMOTE_ADDR' => 'ipaddress' + Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| ip == 'ipaddress' } + @handler.process(req, @response) + end + + describe "with pre-validated certificates" do + + it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" + req = mk_req "myheader" => "/CN=host.domain.com" + @handler.process(req, @response) + end + + it "should retrieve the hostname by matching the certificate parameter" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" + Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| node == "host.domain.com" } + req = mk_req "myheader" => "/CN=host.domain.com" + @handler.process(req, @response) + end + + it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" + req = mk_req "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com" + @handler.process(req, @response) + end + + it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" + Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| authenticated == true } + req = mk_req "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com" + @handler.process(req, @response) + end + + it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" + Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| authenticated == false } + req = mk_req "myheader" => "whatever", "certheader" => "/CN=host.domain.com" + @handler.process(req, @response) + end + + it "should consider the host unauthenticated if no certificate information is present" do + Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" + Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" + Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| authenticated == false } + req = mk_req "myheader" => nil, "certheader" => "/CN=host.domain.com" + @handler.process(req, @response) + end + + it "should resolve the node name with an ip address look-up if no certificate is present" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" + Resolv.any_instance.expects(:getname).returns("host.domain.com") + Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| node == "host.domain.com" } + req = mk_req "myheader" => nil + @handler.process(req, @response) + end + end + end +end diff --git a/spec/unit/network/http/rack/xmlrpc_spec_spec.rb b/spec/unit/network/http/rack/xmlrpc_spec_spec.rb deleted file mode 100755 index abfe84ba6..000000000 --- a/spec/unit/network/http/rack/xmlrpc_spec_spec.rb +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../../spec_helper' -require 'puppet/network/http/rack' if Puppet.features.rack? -require 'puppet/network/http/rack/xmlrpc' if Puppet.features.rack? - -describe "Puppet::Network::HTTP::RackXMLRPC" do - confine "Rack is not available" => Puppet.features.rack? - - describe "when initializing" do - it "should create an Puppet::Network::XMLRPCServer" do - Puppet::Network::XMLRPCServer.expects(:new).returns stub_everything - Puppet::Network::HTTP::RackXMLRPC.new([]) - end - - it "should create each handler" do - handler = stub_everything 'handler' - Puppet::Network::XMLRPCServer.any_instance.stubs(:add_handler) - Puppet::Network::Handler.expects(:handler).returns(handler).times(2) - Puppet::Network::HTTP::RackXMLRPC.new([:foo, :bar]) - end - - it "should add each handler to the XMLRPCserver" do - handler = stub_everything 'handler' - Puppet::Network::Handler.stubs(:handler).returns(handler) - Puppet::Network::XMLRPCServer.any_instance.expects(:add_handler).times(2) - Puppet::Network::HTTP::RackXMLRPC.new([:foo, :bar]) - end - end - - describe "when serving a request" do - - before :each do - foo_handler = stub_everything 'foo_handler' - Puppet::Network::Handler.stubs(:handler).with(:foo).returns foo_handler - Puppet::Network::XMLRPCServer.any_instance.stubs(:add_handler) - Puppet::Network::XMLRPCServer.any_instance.stubs(:process).returns('') - @handler = Puppet::Network::HTTP::RackXMLRPC.new([:foo]) - end - - before :each do - @response = Rack::Response.new() - end - - def mk_req(opts = {}) - opts[:method] = 'POST' if !opts[:method] - opts['CONTENT_TYPE'] = 'text/xml; foo=bar' if !opts['CONTENT_TYPE'] - env = Rack::MockRequest.env_for('/RPC2', opts) - Rack::Request.new(env) - end - - it "should reject non-POST requests" do - req = mk_req :method => 'PUT' - @handler.process(req, @response) - @response.status.should == 405 - end - - it "should reject non text/xml requests" do - req = mk_req 'CONTENT_TYPE' => 'yadda/plain' - end - - it "should create a ClientRequest" do - cr = Puppet::Network::ClientRequest.new(nil, '127.0.0.1', false) - Puppet::Network::ClientRequest.expects(:new).returns cr - req = mk_req - @handler.process(req, @response) - end - - it "should let xmlrpcserver process the request" do - Puppet::Network::XMLRPCServer.any_instance.expects(:process).returns('yay') - req = mk_req - @handler.process(req, @response) - end - - it "should report the response as OK" do - req = mk_req - @handler.process(req, @response) - @response.status.should == 200 - end - - it "should report the response with the correct content type" do - req = mk_req - @handler.process(req, @response) - @response['Content-Type'].should == 'text/xml; charset=utf-8' - end - - it "should set 'authenticated' to false if no certificate is present" do - req = mk_req - Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| authenticated == false } - @handler.process(req, @response) - end - - it "should use the client's ip address" do - req = mk_req 'REMOTE_ADDR' => 'ipaddress' - Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| ip == 'ipaddress' } - @handler.process(req, @response) - end - - describe "with pre-validated certificates" do - - it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" - req = mk_req "myheader" => "/CN=host.domain.com" - @handler.process(req, @response) - end - - it "should retrieve the hostname by matching the certificate parameter" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" - Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| node == "host.domain.com" } - req = mk_req "myheader" => "/CN=host.domain.com" - @handler.process(req, @response) - end - - it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" - req = mk_req "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com" - @handler.process(req, @response) - end - - it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" - Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| authenticated == true } - req = mk_req "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com" - @handler.process(req, @response) - end - - it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" - Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| authenticated == false } - req = mk_req "myheader" => "whatever", "certheader" => "/CN=host.domain.com" - @handler.process(req, @response) - end - - it "should consider the host unauthenticated if no certificate information is present" do - Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" - Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" - Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| authenticated == false } - req = mk_req "myheader" => nil, "certheader" => "/CN=host.domain.com" - @handler.process(req, @response) - end - - it "should resolve the node name with an ip address look-up if no certificate is present" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" - Resolv.any_instance.expects(:getname).returns("host.domain.com") - Puppet::Network::ClientRequest.expects(:new).with() { |node,ip,authenticated| node == "host.domain.com" } - req = mk_req "myheader" => nil - @handler.process(req, @response) - end - end - end -end diff --git a/spec/unit/network/http/rack_spec.rb b/spec/unit/network/http/rack_spec.rb new file mode 100755 index 000000000..cd2961a3a --- /dev/null +++ b/spec/unit/network/http/rack_spec.rb @@ -0,0 +1,102 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/network/http/rack' if Puppet.features.rack? + +describe "Puppet::Network::HTTP::Rack" do + confine "Rack is not available" => Puppet.features.rack? + + describe "while initializing" do + + it "should require a protocol specification" do + Proc.new { Puppet::Network::HTTP::Rack.new({}) }.should raise_error(ArgumentError) + end + + it "should not accept imaginary protocols" do + Proc.new { Puppet::Network::HTTP::Rack.new({:protocols => [:foo]}) }.should raise_error(ArgumentError) + end + + it "should accept the REST protocol" do + Proc.new { Puppet::Network::HTTP::Rack.new({:protocols => [:rest]}) }.should_not raise_error(ArgumentError) + end + + it "should create a RackREST instance" do + Puppet::Network::HTTP::RackREST.expects(:new) + Puppet::Network::HTTP::Rack.new({:protocols => [:rest]}) + end + + describe "with XMLRPC enabled" do + + it "should require XMLRPC handlers" do + Proc.new { Puppet::Network::HTTP::Rack.new({:protocols => [:xmlrpc]}) }.should raise_error(ArgumentError) + end + + it "should create a RackXMLRPC instance" do + Puppet::Network::HTTP::RackXMLRPC.expects(:new) + Puppet::Network::HTTP::Rack.new({:protocols => [:xmlrpc], :xmlrpc_handlers => [:Status]}) + end + + end + + end + + describe "when called" do + + before :all do + @app = Puppet::Network::HTTP::Rack.new({:protocols => [:rest]}) + # let's use Rack::Lint to verify that we're OK with the rack specification + @linted = Rack::Lint.new(@app) + end + + before :each do + @env = Rack::MockRequest.env_for('/') + end + + it "should create a Request object" do + request = Rack::Request.new(@env) + Rack::Request.expects(:new).returns request + @linted.call(@env) + end + + it "should create a Response object" do + Rack::Response.expects(:new).returns stub_everything + @app.call(@env) # can't lint when Rack::Response is a stub + end + + it "should let RackREST process the request" do + Puppet::Network::HTTP::RackREST.any_instance.expects(:process).once + @linted.call(@env) + end + + it "should catch unhandled exceptions from RackREST" do + Puppet::Network::HTTP::RackREST.any_instance.expects(:process).raises(ArgumentError, 'test error') + Proc.new { @linted.call(@env) }.should_not raise_error + end + + it "should finish() the Response" do + Rack::Response.any_instance.expects(:finish).once + @app.call(@env) # can't lint when finish is a stub + end + + end + + describe "when serving XMLRPC" do + + before :all do + @app = Puppet::Network::HTTP::Rack.new({:protocols => [:rest, :xmlrpc], :xmlrpc_handlers => [:Status]}) + @linted = Rack::Lint.new(@app) + end + + before :each do + @env = Rack::MockRequest.env_for('/RPC2', :method => 'POST') + end + + it "should use RackXMLRPC to serve /RPC2 requests" do + Puppet::Network::HTTP::RackXMLRPC.any_instance.expects(:process).once + @linted.call(@env) + end + + end + +end + diff --git a/spec/unit/network/http/rack_spec_spec.rb b/spec/unit/network/http/rack_spec_spec.rb deleted file mode 100755 index cd2961a3a..000000000 --- a/spec/unit/network/http/rack_spec_spec.rb +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/network/http/rack' if Puppet.features.rack? - -describe "Puppet::Network::HTTP::Rack" do - confine "Rack is not available" => Puppet.features.rack? - - describe "while initializing" do - - it "should require a protocol specification" do - Proc.new { Puppet::Network::HTTP::Rack.new({}) }.should raise_error(ArgumentError) - end - - it "should not accept imaginary protocols" do - Proc.new { Puppet::Network::HTTP::Rack.new({:protocols => [:foo]}) }.should raise_error(ArgumentError) - end - - it "should accept the REST protocol" do - Proc.new { Puppet::Network::HTTP::Rack.new({:protocols => [:rest]}) }.should_not raise_error(ArgumentError) - end - - it "should create a RackREST instance" do - Puppet::Network::HTTP::RackREST.expects(:new) - Puppet::Network::HTTP::Rack.new({:protocols => [:rest]}) - end - - describe "with XMLRPC enabled" do - - it "should require XMLRPC handlers" do - Proc.new { Puppet::Network::HTTP::Rack.new({:protocols => [:xmlrpc]}) }.should raise_error(ArgumentError) - end - - it "should create a RackXMLRPC instance" do - Puppet::Network::HTTP::RackXMLRPC.expects(:new) - Puppet::Network::HTTP::Rack.new({:protocols => [:xmlrpc], :xmlrpc_handlers => [:Status]}) - end - - end - - end - - describe "when called" do - - before :all do - @app = Puppet::Network::HTTP::Rack.new({:protocols => [:rest]}) - # let's use Rack::Lint to verify that we're OK with the rack specification - @linted = Rack::Lint.new(@app) - end - - before :each do - @env = Rack::MockRequest.env_for('/') - end - - it "should create a Request object" do - request = Rack::Request.new(@env) - Rack::Request.expects(:new).returns request - @linted.call(@env) - end - - it "should create a Response object" do - Rack::Response.expects(:new).returns stub_everything - @app.call(@env) # can't lint when Rack::Response is a stub - end - - it "should let RackREST process the request" do - Puppet::Network::HTTP::RackREST.any_instance.expects(:process).once - @linted.call(@env) - end - - it "should catch unhandled exceptions from RackREST" do - Puppet::Network::HTTP::RackREST.any_instance.expects(:process).raises(ArgumentError, 'test error') - Proc.new { @linted.call(@env) }.should_not raise_error - end - - it "should finish() the Response" do - Rack::Response.any_instance.expects(:finish).once - @app.call(@env) # can't lint when finish is a stub - end - - end - - describe "when serving XMLRPC" do - - before :all do - @app = Puppet::Network::HTTP::Rack.new({:protocols => [:rest, :xmlrpc], :xmlrpc_handlers => [:Status]}) - @linted = Rack::Lint.new(@app) - end - - before :each do - @env = Rack::MockRequest.env_for('/RPC2', :method => 'POST') - end - - it "should use RackXMLRPC to serve /RPC2 requests" do - Puppet::Network::HTTP::RackXMLRPC.any_instance.expects(:process).once - @linted.call(@env) - end - - end - -end - diff --git a/spec/unit/network/http/webrick/rest_spec.rb b/spec/unit/network/http/webrick/rest_spec.rb new file mode 100755 index 000000000..f726fd9a7 --- /dev/null +++ b/spec/unit/network/http/webrick/rest_spec.rb @@ -0,0 +1,180 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../../spec_helper' +require 'puppet/network/http' +require 'puppet/network/http/webrick/rest' + +describe Puppet::Network::HTTP::WEBrickREST do + it "should include the Puppet::Network::HTTP::Handler module" do + Puppet::Network::HTTP::WEBrickREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) + end + + describe "when initializing" do + it "should call the Handler's initialization hook with its provided arguments as the server and handler" do + Puppet::Network::HTTP::WEBrickREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") + Puppet::Network::HTTP::WEBrickREST.new("my", "arguments") + end + end + + describe "when receiving a request" do + before do + @request = stub('webrick http request', :query => {}, :peeraddr => %w{eh boo host ip}, :client_cert => nil) + @response = stub('webrick http response', :status= => true, :body= => true) + @model_class = stub('indirected model class') + @webrick = stub('webrick http server', :mount => true, :[] => {}) + Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) + @handler = Puppet::Network::HTTP::WEBrickREST.new(@webrick, :foo) + end + + it "should delegate its :service method to its :process method" do + @handler.expects(:process).with(@request, @response).returns "stuff" + @handler.service(@request, @response).should == "stuff" + end + + describe "when using the Handler interface" do + it "should use the 'accept' request parameter as the Accept header" do + @request.expects(:[]).with("accept").returns "foobar" + @handler.accept_header(@request).should == "foobar" + end + + it "should use the 'content-type' request header as the Content-Type header" do + @request.expects(:[]).with("content-type").returns "foobar" + @handler.content_type_header(@request).should == "foobar" + end + + it "should use the request method as the http method" do + @request.expects(:request_method).returns "FOO" + @handler.http_method(@request).should == "FOO" + end + + it "should return the request path as the path" do + @request.expects(:path).returns "/foo/bar" + @handler.path(@request).should == "/foo/bar" + end + + it "should return the request body as the body" do + @request.expects(:body).returns "my body" + @handler.body(@request).should == "my body" + end + + it "should set the response's 'content-type' header when setting the content type" do + @response.expects(:[]=).with("content-type", "text/html") + @handler.set_content_type(@response, "text/html") + end + + it "should set the status and body on the response when setting the response for a successful query" do + @response.expects(:status=).with 200 + @response.expects(:body=).with "mybody" + + @handler.set_response(@response, "mybody", 200) + end + + describe "when the result is a File" do + before(:each) do + stat = stub 'stat', :size => 100 + @file = stub 'file', :stat => stat, :path => "/tmp/path" + @file.stubs(:is_a?).with(File).returns(true) + end + + it "should serve it" do + @response.stubs(:[]=) + + @response.expects(:status=).with 200 + @response.expects(:body=).with @file + + @handler.set_response(@response, @file, 200) + end + + it "should set the Content-Length header" do + @response.expects(:[]=).with('content-length', 100) + + @handler.set_response(@response, @file, 200) + end + end + + it "should set the status and message on the response when setting the response for a failed query" do + @response.expects(:status=).with 400 + @response.expects(:reason_phrase=).with "mybody" + + @handler.set_response(@response, "mybody", 400) + end + end + + describe "and determining the request parameters" do + it "should include the HTTP request parameters, with the keys as symbols" do + @request.stubs(:query).returns("foo" => "baz", "bar" => "xyzzy") + result = @handler.params(@request) + result[:foo].should == "baz" + result[:bar].should == "xyzzy" + end + + it "should CGI-decode the HTTP parameters" do + encoding = CGI.escape("foo bar") + @request.expects(:query).returns('foo' => encoding) + result = @handler.params(@request) + result[:foo].should == "foo bar" + end + + it "should convert the string 'true' to the boolean" do + @request.expects(:query).returns('foo' => "true") + result = @handler.params(@request) + result[:foo].should be_true + end + + it "should convert the string 'false' to the boolean" do + @request.expects(:query).returns('foo' => "false") + result = @handler.params(@request) + result[:foo].should be_false + end + + it "should YAML-load and CGI-decode values that are YAML-encoded" do + escaping = CGI.escape(YAML.dump(%w{one two})) + @request.expects(:query).returns('foo' => escaping) + result = @handler.params(@request) + result[:foo].should == %w{one two} + end + + it "should not allow clients to set the node via the request parameters" do + @request.stubs(:query).returns("node" => "foo") + @handler.stubs(:resolve_node) + + @handler.params(@request)[:node].should be_nil + end + + it "should not allow clients to set the IP via the request parameters" do + @request.stubs(:query).returns("ip" => "foo") + @handler.params(@request)[:ip].should_not == "foo" + end + + it "should pass the client's ip address to model find" do + @request.stubs(:peeraddr).returns(%w{noidea dunno hostname ipaddress}) + @handler.params(@request)[:ip].should == "ipaddress" + end + + it "should set 'authenticated' to true if a certificate is present" do + cert = stub 'cert', :subject => [%w{CN host.domain.com}] + @request.stubs(:client_cert).returns cert + @handler.params(@request)[:authenticated].should be_true + end + + it "should set 'authenticated' to false if no certificate is present" do + @request.stubs(:client_cert).returns nil + @handler.params(@request)[:authenticated].should be_false + end + + it "should pass the client's certificate name to model method if a certificate is present" do + cert = stub 'cert', :subject => [%w{CN host.domain.com}] + @request.stubs(:client_cert).returns cert + @handler.params(@request)[:node].should == "host.domain.com" + end + + it "should resolve the node name with an ip address look-up if no certificate is present" do + @request.stubs(:client_cert).returns nil + + @handler.expects(:resolve_node).returns(:resolved_node) + + @handler.params(@request)[:node].should == :resolved_node + end + end + end +end diff --git a/spec/unit/network/http/webrick/rest_spec_spec.rb b/spec/unit/network/http/webrick/rest_spec_spec.rb deleted file mode 100755 index f726fd9a7..000000000 --- a/spec/unit/network/http/webrick/rest_spec_spec.rb +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../../spec_helper' -require 'puppet/network/http' -require 'puppet/network/http/webrick/rest' - -describe Puppet::Network::HTTP::WEBrickREST do - it "should include the Puppet::Network::HTTP::Handler module" do - Puppet::Network::HTTP::WEBrickREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) - end - - describe "when initializing" do - it "should call the Handler's initialization hook with its provided arguments as the server and handler" do - Puppet::Network::HTTP::WEBrickREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") - Puppet::Network::HTTP::WEBrickREST.new("my", "arguments") - end - end - - describe "when receiving a request" do - before do - @request = stub('webrick http request', :query => {}, :peeraddr => %w{eh boo host ip}, :client_cert => nil) - @response = stub('webrick http response', :status= => true, :body= => true) - @model_class = stub('indirected model class') - @webrick = stub('webrick http server', :mount => true, :[] => {}) - Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) - @handler = Puppet::Network::HTTP::WEBrickREST.new(@webrick, :foo) - end - - it "should delegate its :service method to its :process method" do - @handler.expects(:process).with(@request, @response).returns "stuff" - @handler.service(@request, @response).should == "stuff" - end - - describe "when using the Handler interface" do - it "should use the 'accept' request parameter as the Accept header" do - @request.expects(:[]).with("accept").returns "foobar" - @handler.accept_header(@request).should == "foobar" - end - - it "should use the 'content-type' request header as the Content-Type header" do - @request.expects(:[]).with("content-type").returns "foobar" - @handler.content_type_header(@request).should == "foobar" - end - - it "should use the request method as the http method" do - @request.expects(:request_method).returns "FOO" - @handler.http_method(@request).should == "FOO" - end - - it "should return the request path as the path" do - @request.expects(:path).returns "/foo/bar" - @handler.path(@request).should == "/foo/bar" - end - - it "should return the request body as the body" do - @request.expects(:body).returns "my body" - @handler.body(@request).should == "my body" - end - - it "should set the response's 'content-type' header when setting the content type" do - @response.expects(:[]=).with("content-type", "text/html") - @handler.set_content_type(@response, "text/html") - end - - it "should set the status and body on the response when setting the response for a successful query" do - @response.expects(:status=).with 200 - @response.expects(:body=).with "mybody" - - @handler.set_response(@response, "mybody", 200) - end - - describe "when the result is a File" do - before(:each) do - stat = stub 'stat', :size => 100 - @file = stub 'file', :stat => stat, :path => "/tmp/path" - @file.stubs(:is_a?).with(File).returns(true) - end - - it "should serve it" do - @response.stubs(:[]=) - - @response.expects(:status=).with 200 - @response.expects(:body=).with @file - - @handler.set_response(@response, @file, 200) - end - - it "should set the Content-Length header" do - @response.expects(:[]=).with('content-length', 100) - - @handler.set_response(@response, @file, 200) - end - end - - it "should set the status and message on the response when setting the response for a failed query" do - @response.expects(:status=).with 400 - @response.expects(:reason_phrase=).with "mybody" - - @handler.set_response(@response, "mybody", 400) - end - end - - describe "and determining the request parameters" do - it "should include the HTTP request parameters, with the keys as symbols" do - @request.stubs(:query).returns("foo" => "baz", "bar" => "xyzzy") - result = @handler.params(@request) - result[:foo].should == "baz" - result[:bar].should == "xyzzy" - end - - it "should CGI-decode the HTTP parameters" do - encoding = CGI.escape("foo bar") - @request.expects(:query).returns('foo' => encoding) - result = @handler.params(@request) - result[:foo].should == "foo bar" - end - - it "should convert the string 'true' to the boolean" do - @request.expects(:query).returns('foo' => "true") - result = @handler.params(@request) - result[:foo].should be_true - end - - it "should convert the string 'false' to the boolean" do - @request.expects(:query).returns('foo' => "false") - result = @handler.params(@request) - result[:foo].should be_false - end - - it "should YAML-load and CGI-decode values that are YAML-encoded" do - escaping = CGI.escape(YAML.dump(%w{one two})) - @request.expects(:query).returns('foo' => escaping) - result = @handler.params(@request) - result[:foo].should == %w{one two} - end - - it "should not allow clients to set the node via the request parameters" do - @request.stubs(:query).returns("node" => "foo") - @handler.stubs(:resolve_node) - - @handler.params(@request)[:node].should be_nil - end - - it "should not allow clients to set the IP via the request parameters" do - @request.stubs(:query).returns("ip" => "foo") - @handler.params(@request)[:ip].should_not == "foo" - end - - it "should pass the client's ip address to model find" do - @request.stubs(:peeraddr).returns(%w{noidea dunno hostname ipaddress}) - @handler.params(@request)[:ip].should == "ipaddress" - end - - it "should set 'authenticated' to true if a certificate is present" do - cert = stub 'cert', :subject => [%w{CN host.domain.com}] - @request.stubs(:client_cert).returns cert - @handler.params(@request)[:authenticated].should be_true - end - - it "should set 'authenticated' to false if no certificate is present" do - @request.stubs(:client_cert).returns nil - @handler.params(@request)[:authenticated].should be_false - end - - it "should pass the client's certificate name to model method if a certificate is present" do - cert = stub 'cert', :subject => [%w{CN host.domain.com}] - @request.stubs(:client_cert).returns cert - @handler.params(@request)[:node].should == "host.domain.com" - end - - it "should resolve the node name with an ip address look-up if no certificate is present" do - @request.stubs(:client_cert).returns nil - - @handler.expects(:resolve_node).returns(:resolved_node) - - @handler.params(@request)[:node].should == :resolved_node - end - end - end -end diff --git a/spec/unit/network/http/webrick/xmlrpc_spec.rb b/spec/unit/network/http/webrick/xmlrpc_spec.rb new file mode 100755 index 000000000..e69de29bb diff --git a/spec/unit/network/http/webrick/xmlrpc_spec_spec.rb b/spec/unit/network/http/webrick/xmlrpc_spec_spec.rb deleted file mode 100755 index e69de29bb..000000000 diff --git a/spec/unit/network/http/webrick_spec.rb b/spec/unit/network/http/webrick_spec.rb new file mode 100755 index 000000000..aaab53b0c --- /dev/null +++ b/spec/unit/network/http/webrick_spec.rb @@ -0,0 +1,339 @@ +#!/usr/bin/env ruby +# +# Created by Rick Bradley on 2007-10-15. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/network/http' +require 'puppet/network/http/webrick' + +describe Puppet::Network::HTTP::WEBrick, "after initializing" do + it "should not be listening" do + Puppet::Network::HTTP::WEBrick.new.should_not be_listening + end +end + +describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do + before do + @mock_webrick = stub('webrick', :[] => {}, :listeners => [], :status => :Running) + [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} + WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) + @server = Puppet::Network::HTTP::WEBrick.new + [:setup_logger, :setup_ssl].each {|meth| @server.stubs(meth).returns({})} # the empty hash is required because of how we're merging + @listen_params = { :address => "127.0.0.1", :port => 31337, :xmlrpc_handlers => [], :protocols => [ :rest ] } + end + + it "should fail if already listening" do + @server.listen(@listen_params) + Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError) + end + + it "should require at least one protocol" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :protocols == k}) }.should raise_error(ArgumentError) + end + + it "should require a listening address to be specified" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) + end + + it "should require a listening port to be specified" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError) + end + + it "should order a webrick server to start in a separate thread" do + @mock_webrick.expects(:start) + # If you remove this you'll sometimes get race condition problems + Thread.expects(:new).yields + @server.listen(@listen_params) + end + + it "should tell webrick to listen on the specified address and port" do + WEBrick::HTTPServer.expects(:new).with {|args| + args[:Port] == 31337 and args[:BindAddress] == "127.0.0.1" + }.returns(@mock_webrick) + @server.listen(@listen_params) + end + + it "should configure a logger for webrick" do + @server.expects(:setup_logger).returns(:Logger => :mylogger) + + WEBrick::HTTPServer.expects(:new).with {|args| + args[:Logger] == :mylogger + }.returns(@mock_webrick) + + @server.listen(@listen_params) + end + + it "should configure SSL for webrick" do + @server.expects(:setup_ssl).returns(:Ssl => :testing, :Other => :yay) + + WEBrick::HTTPServer.expects(:new).with {|args| + args[:Ssl] == :testing and args[:Other] == :yay + }.returns(@mock_webrick) + + @server.listen(@listen_params) + end + + it "should be listening" do + @server.listen(@listen_params) + @server.should be_listening + end + + describe "when the REST protocol is requested" do + it "should register the REST handler at /" do + # We don't care about the options here. + @mock_webrick.expects(:mount).with { |path, klass, options| path == "/" and klass == Puppet::Network::HTTP::WEBrickREST } + + @server.listen(@listen_params.merge(:protocols => [:rest])) + end + end + + describe "when the XMLRPC protocol is requested" do + before do + @servlet = mock 'servlet' + + Puppet::Network::XMLRPC::WEBrickServlet.stubs(:new).returns @servlet + + @master_handler = mock('master_handler') + @file_handler = mock('file_handler') + + @master = mock 'master' + @file = mock 'file' + @master_handler.stubs(:new).returns @master + @file_handler.stubs(:new).returns @file + + Puppet::Network::Handler.stubs(:handler).with(:master).returns @master_handler + Puppet::Network::Handler.stubs(:handler).with(:fileserver).returns @file_handler + end + + it "should do nothing if no xmlrpc handlers have been specified" do + Puppet::Network::Handler.expects(:handler).never + + @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [])) + end + + it "should look the handler classes up via their base class" do + Puppet::Network::Handler.expects(:handler).with(:master).returns @master_handler + Puppet::Network::Handler.expects(:handler).with(:fileserver).returns @file_handler + + @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) + end + + it "should create an instance for each requested xmlrpc handler" do + @master_handler.expects(:new).returns @master + @file_handler.expects(:new).returns @file + + @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) + end + + it "should create a webrick servlet with the xmlrpc handler instances" do + Puppet::Network::XMLRPC::WEBrickServlet.expects(:new).with([@master, @file]).returns @servlet + + @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) + end + + it "should mount the webrick servlet at /RPC2" do + @mock_webrick.stubs(:mount) + @mock_webrick.expects(:mount).with("/RPC2", @servlet) + + @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) + end + end +end + + +describe Puppet::Network::HTTP::WEBrick, "when looking up the class to handle a protocol" do + it "should require a protocol" do + lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol }.should raise_error(ArgumentError) + end + + it "should accept a protocol" do + lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol("bob") }.should_not raise_error(ArgumentError) + end + + it "should use a WEBrick + REST class when a REST protocol is specified" do + Puppet::Network::HTTP::WEBrick.class_for_protocol("rest").should == Puppet::Network::HTTP::WEBrickREST + end + + it "should fail when an unknown protocol is specified" do + lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol("abcdefg") }.should raise_error + end +end + +describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do + before do + @mock_webrick = stub('webrick', :[] => {}, :listeners => [], :status => :Running) + [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} + WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) + @server = Puppet::Network::HTTP::WEBrick.new + [:setup_logger, :setup_ssl].each {|meth| @server.stubs(meth).returns({})} # the empty hash is required because of how we're merging + @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] } + end + + it "should fail unless listening" do + Proc.new { @server.unlisten }.should raise_error(RuntimeError) + end + + it "should order webrick server to stop" do + @mock_webrick.expects(:shutdown) + @server.listen(@listen_params) + @server.unlisten + end + + it "should no longer be listening" do + @server.listen(@listen_params) + @server.unlisten + @server.should_not be_listening + end +end + +describe Puppet::Network::HTTP::WEBrick do + before do + @mock_webrick = stub('webrick', :[] => {}) + [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} + WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) + @server = Puppet::Network::HTTP::WEBrick.new + end + + describe "when configuring an http logger" do + before do + Puppet.settings.stubs(:value).returns "something" + Puppet.settings.stubs(:use) + @filehandle = stub 'handle', :fcntl => nil, :sync => nil + + File.stubs(:open).returns @filehandle + end + + it "should use the settings for :main, :ssl, and the process name" do + Puppet.settings.stubs(:value).with(:name).returns "myname" + Puppet.settings.expects(:use).with(:main, :ssl, "myname") + + @server.setup_logger + end + + it "should use the masterlog if the process mode is master" do + Puppet.mode.stubs(:master?).returns(true) + Puppet.settings.expects(:value).with(:masterhttplog).returns "/master/log" + + File.expects(:open).with("/master/log", "a+").returns @filehandle + + @server.setup_logger + end + + it "should use the httplog if the process mode is not master" do + Puppet.mode.stubs(:master?).returns(false) + Puppet.settings.expects(:value).with(:httplog).returns "/other/log" + + File.expects(:open).with("/other/log", "a+").returns @filehandle + + @server.setup_logger + end + + describe "and creating the logging filehandle" do + it "should set fcntl to 'Fcntl::F_SETFD, Fcntl::FD_CLOEXEC'" do + @filehandle.expects(:fcntl).with(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) + + @server.setup_logger + end + + it "should sync the filehandle" do + @filehandle.expects(:sync) + + @server.setup_logger + end + end + + it "should create a new WEBrick::Log instance with the open filehandle" do + WEBrick::Log.expects(:new).with(@filehandle) + + @server.setup_logger + end + + it "should set debugging if the current loglevel is :debug" do + Puppet::Util::Log.expects(:level).returns :debug + + WEBrick::Log.expects(:new).with { |handle, debug| debug == WEBrick::Log::DEBUG } + + @server.setup_logger + end + + it "should return the logger as the main log" do + logger = mock 'logger' + WEBrick::Log.expects(:new).returns logger + + @server.setup_logger[:Logger].should == logger + end + + it "should return the logger as the access log using both the Common and Referer log format" do + logger = mock 'logger' + WEBrick::Log.expects(:new).returns logger + + @server.setup_logger[:AccessLog].should == [ + [logger, WEBrick::AccessLog::COMMON_LOG_FORMAT], + [logger, WEBrick::AccessLog::REFERER_LOG_FORMAT] + ] + end + end + + describe "when configuring ssl" do + before do + @key = stub 'key', :content => "mykey" + @cert = stub 'cert', :content => "mycert" + @host = stub 'host', :key => @key, :certificate => @cert, :name => "yay", :ssl_store => "mystore" + + Puppet::SSL::Certificate.stubs(:find).with('ca').returns @cert + + Puppet::SSL::Host.stubs(:localhost).returns @host + end + + it "should use the key from the localhost SSL::Host instance" do + Puppet::SSL::Host.expects(:localhost).returns @host + @host.expects(:key).returns @key + + @server.setup_ssl[:SSLPrivateKey].should == "mykey" + end + + it "should configure the certificate" do + @server.setup_ssl[:SSLCertificate].should == "mycert" + end + + it "should fail if no CA certificate can be found" do + Puppet::SSL::Certificate.stubs(:find).with('ca').returns nil + + lambda { @server.setup_ssl }.should raise_error(Puppet::Error) + end + + it "should specify the path to the CA certificate" do + Puppet.settings.stubs(:value).returns "whatever" + Puppet.settings.stubs(:value).with(:hostcrl).returns 'false' + Puppet.settings.stubs(:value).with(:localcacert).returns '/ca/crt' + + @server.setup_ssl[:SSLCACertificateFile].should == "/ca/crt" + end + + it "should start ssl immediately" do + @server.setup_ssl[:SSLStartImmediately].should be_true + end + + it "should enable ssl" do + @server.setup_ssl[:SSLEnable].should be_true + end + + it "should configure the verification method as 'OpenSSL::SSL::VERIFY_PEER'" do + @server.setup_ssl[:SSLVerifyClient].should == OpenSSL::SSL::VERIFY_PEER + end + + it "should add an x509 store" do + Puppet.settings.stubs(:value).returns "whatever" + Puppet.settings.stubs(:value).with(:hostcrl).returns '/my/crl' + + @host.expects(:ssl_store).returns "mystore" + + @server.setup_ssl[:SSLCertificateStore].should == "mystore" + end + + it "should set the certificate name to 'nil'" do + @server.setup_ssl[:SSLCertName].should be_nil + end + end +end diff --git a/spec/unit/network/http/webrick_spec_spec.rb b/spec/unit/network/http/webrick_spec_spec.rb deleted file mode 100755 index aaab53b0c..000000000 --- a/spec/unit/network/http/webrick_spec_spec.rb +++ /dev/null @@ -1,339 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Rick Bradley on 2007-10-15. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/network/http' -require 'puppet/network/http/webrick' - -describe Puppet::Network::HTTP::WEBrick, "after initializing" do - it "should not be listening" do - Puppet::Network::HTTP::WEBrick.new.should_not be_listening - end -end - -describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do - before do - @mock_webrick = stub('webrick', :[] => {}, :listeners => [], :status => :Running) - [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} - WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) - @server = Puppet::Network::HTTP::WEBrick.new - [:setup_logger, :setup_ssl].each {|meth| @server.stubs(meth).returns({})} # the empty hash is required because of how we're merging - @listen_params = { :address => "127.0.0.1", :port => 31337, :xmlrpc_handlers => [], :protocols => [ :rest ] } - end - - it "should fail if already listening" do - @server.listen(@listen_params) - Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError) - end - - it "should require at least one protocol" do - Proc.new { @server.listen(@listen_params.delete_if {|k,v| :protocols == k}) }.should raise_error(ArgumentError) - end - - it "should require a listening address to be specified" do - Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) - end - - it "should require a listening port to be specified" do - Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError) - end - - it "should order a webrick server to start in a separate thread" do - @mock_webrick.expects(:start) - # If you remove this you'll sometimes get race condition problems - Thread.expects(:new).yields - @server.listen(@listen_params) - end - - it "should tell webrick to listen on the specified address and port" do - WEBrick::HTTPServer.expects(:new).with {|args| - args[:Port] == 31337 and args[:BindAddress] == "127.0.0.1" - }.returns(@mock_webrick) - @server.listen(@listen_params) - end - - it "should configure a logger for webrick" do - @server.expects(:setup_logger).returns(:Logger => :mylogger) - - WEBrick::HTTPServer.expects(:new).with {|args| - args[:Logger] == :mylogger - }.returns(@mock_webrick) - - @server.listen(@listen_params) - end - - it "should configure SSL for webrick" do - @server.expects(:setup_ssl).returns(:Ssl => :testing, :Other => :yay) - - WEBrick::HTTPServer.expects(:new).with {|args| - args[:Ssl] == :testing and args[:Other] == :yay - }.returns(@mock_webrick) - - @server.listen(@listen_params) - end - - it "should be listening" do - @server.listen(@listen_params) - @server.should be_listening - end - - describe "when the REST protocol is requested" do - it "should register the REST handler at /" do - # We don't care about the options here. - @mock_webrick.expects(:mount).with { |path, klass, options| path == "/" and klass == Puppet::Network::HTTP::WEBrickREST } - - @server.listen(@listen_params.merge(:protocols => [:rest])) - end - end - - describe "when the XMLRPC protocol is requested" do - before do - @servlet = mock 'servlet' - - Puppet::Network::XMLRPC::WEBrickServlet.stubs(:new).returns @servlet - - @master_handler = mock('master_handler') - @file_handler = mock('file_handler') - - @master = mock 'master' - @file = mock 'file' - @master_handler.stubs(:new).returns @master - @file_handler.stubs(:new).returns @file - - Puppet::Network::Handler.stubs(:handler).with(:master).returns @master_handler - Puppet::Network::Handler.stubs(:handler).with(:fileserver).returns @file_handler - end - - it "should do nothing if no xmlrpc handlers have been specified" do - Puppet::Network::Handler.expects(:handler).never - - @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [])) - end - - it "should look the handler classes up via their base class" do - Puppet::Network::Handler.expects(:handler).with(:master).returns @master_handler - Puppet::Network::Handler.expects(:handler).with(:fileserver).returns @file_handler - - @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) - end - - it "should create an instance for each requested xmlrpc handler" do - @master_handler.expects(:new).returns @master - @file_handler.expects(:new).returns @file - - @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) - end - - it "should create a webrick servlet with the xmlrpc handler instances" do - Puppet::Network::XMLRPC::WEBrickServlet.expects(:new).with([@master, @file]).returns @servlet - - @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) - end - - it "should mount the webrick servlet at /RPC2" do - @mock_webrick.stubs(:mount) - @mock_webrick.expects(:mount).with("/RPC2", @servlet) - - @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) - end - end -end - - -describe Puppet::Network::HTTP::WEBrick, "when looking up the class to handle a protocol" do - it "should require a protocol" do - lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol }.should raise_error(ArgumentError) - end - - it "should accept a protocol" do - lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol("bob") }.should_not raise_error(ArgumentError) - end - - it "should use a WEBrick + REST class when a REST protocol is specified" do - Puppet::Network::HTTP::WEBrick.class_for_protocol("rest").should == Puppet::Network::HTTP::WEBrickREST - end - - it "should fail when an unknown protocol is specified" do - lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol("abcdefg") }.should raise_error - end -end - -describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do - before do - @mock_webrick = stub('webrick', :[] => {}, :listeners => [], :status => :Running) - [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} - WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) - @server = Puppet::Network::HTTP::WEBrick.new - [:setup_logger, :setup_ssl].each {|meth| @server.stubs(meth).returns({})} # the empty hash is required because of how we're merging - @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] } - end - - it "should fail unless listening" do - Proc.new { @server.unlisten }.should raise_error(RuntimeError) - end - - it "should order webrick server to stop" do - @mock_webrick.expects(:shutdown) - @server.listen(@listen_params) - @server.unlisten - end - - it "should no longer be listening" do - @server.listen(@listen_params) - @server.unlisten - @server.should_not be_listening - end -end - -describe Puppet::Network::HTTP::WEBrick do - before do - @mock_webrick = stub('webrick', :[] => {}) - [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} - WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) - @server = Puppet::Network::HTTP::WEBrick.new - end - - describe "when configuring an http logger" do - before do - Puppet.settings.stubs(:value).returns "something" - Puppet.settings.stubs(:use) - @filehandle = stub 'handle', :fcntl => nil, :sync => nil - - File.stubs(:open).returns @filehandle - end - - it "should use the settings for :main, :ssl, and the process name" do - Puppet.settings.stubs(:value).with(:name).returns "myname" - Puppet.settings.expects(:use).with(:main, :ssl, "myname") - - @server.setup_logger - end - - it "should use the masterlog if the process mode is master" do - Puppet.mode.stubs(:master?).returns(true) - Puppet.settings.expects(:value).with(:masterhttplog).returns "/master/log" - - File.expects(:open).with("/master/log", "a+").returns @filehandle - - @server.setup_logger - end - - it "should use the httplog if the process mode is not master" do - Puppet.mode.stubs(:master?).returns(false) - Puppet.settings.expects(:value).with(:httplog).returns "/other/log" - - File.expects(:open).with("/other/log", "a+").returns @filehandle - - @server.setup_logger - end - - describe "and creating the logging filehandle" do - it "should set fcntl to 'Fcntl::F_SETFD, Fcntl::FD_CLOEXEC'" do - @filehandle.expects(:fcntl).with(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) - - @server.setup_logger - end - - it "should sync the filehandle" do - @filehandle.expects(:sync) - - @server.setup_logger - end - end - - it "should create a new WEBrick::Log instance with the open filehandle" do - WEBrick::Log.expects(:new).with(@filehandle) - - @server.setup_logger - end - - it "should set debugging if the current loglevel is :debug" do - Puppet::Util::Log.expects(:level).returns :debug - - WEBrick::Log.expects(:new).with { |handle, debug| debug == WEBrick::Log::DEBUG } - - @server.setup_logger - end - - it "should return the logger as the main log" do - logger = mock 'logger' - WEBrick::Log.expects(:new).returns logger - - @server.setup_logger[:Logger].should == logger - end - - it "should return the logger as the access log using both the Common and Referer log format" do - logger = mock 'logger' - WEBrick::Log.expects(:new).returns logger - - @server.setup_logger[:AccessLog].should == [ - [logger, WEBrick::AccessLog::COMMON_LOG_FORMAT], - [logger, WEBrick::AccessLog::REFERER_LOG_FORMAT] - ] - end - end - - describe "when configuring ssl" do - before do - @key = stub 'key', :content => "mykey" - @cert = stub 'cert', :content => "mycert" - @host = stub 'host', :key => @key, :certificate => @cert, :name => "yay", :ssl_store => "mystore" - - Puppet::SSL::Certificate.stubs(:find).with('ca').returns @cert - - Puppet::SSL::Host.stubs(:localhost).returns @host - end - - it "should use the key from the localhost SSL::Host instance" do - Puppet::SSL::Host.expects(:localhost).returns @host - @host.expects(:key).returns @key - - @server.setup_ssl[:SSLPrivateKey].should == "mykey" - end - - it "should configure the certificate" do - @server.setup_ssl[:SSLCertificate].should == "mycert" - end - - it "should fail if no CA certificate can be found" do - Puppet::SSL::Certificate.stubs(:find).with('ca').returns nil - - lambda { @server.setup_ssl }.should raise_error(Puppet::Error) - end - - it "should specify the path to the CA certificate" do - Puppet.settings.stubs(:value).returns "whatever" - Puppet.settings.stubs(:value).with(:hostcrl).returns 'false' - Puppet.settings.stubs(:value).with(:localcacert).returns '/ca/crt' - - @server.setup_ssl[:SSLCACertificateFile].should == "/ca/crt" - end - - it "should start ssl immediately" do - @server.setup_ssl[:SSLStartImmediately].should be_true - end - - it "should enable ssl" do - @server.setup_ssl[:SSLEnable].should be_true - end - - it "should configure the verification method as 'OpenSSL::SSL::VERIFY_PEER'" do - @server.setup_ssl[:SSLVerifyClient].should == OpenSSL::SSL::VERIFY_PEER - end - - it "should add an x509 store" do - Puppet.settings.stubs(:value).returns "whatever" - Puppet.settings.stubs(:value).with(:hostcrl).returns '/my/crl' - - @host.expects(:ssl_store).returns "mystore" - - @server.setup_ssl[:SSLCertificateStore].should == "mystore" - end - - it "should set the certificate name to 'nil'" do - @server.setup_ssl[:SSLCertName].should be_nil - end - end -end diff --git a/spec/unit/network/http_pool_spec.rb b/spec/unit/network/http_pool_spec.rb new file mode 100755 index 000000000..7fe55c5fa --- /dev/null +++ b/spec/unit/network/http_pool_spec.rb @@ -0,0 +1,206 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-11-26. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/network/http_pool' + +describe Puppet::Network::HttpPool do + after do + Puppet::Util::Cacher.expire + Puppet::Network::HttpPool.clear_http_instances + Puppet::Network::HttpPool.instance_variable_set("@ssl_host", nil) + end + + it "should have keep-alive disabled" do + Puppet::Network::HttpPool::HTTP_KEEP_ALIVE.should be_false + end + + it "should use the global SSL::Host instance to get its certificate information" do + host = mock 'host' + Puppet::SSL::Host.expects(:localhost).with().returns host + Puppet::Network::HttpPool.ssl_host.should equal(host) + end + + describe "when managing http instances" do + def stub_settings(settings) + settings.each do |param, value| + Puppet.settings.stubs(:value).with(param).returns(value) + end + end + + before do + # All of the cert stuff is tested elsewhere + Puppet::Network::HttpPool.stubs(:cert_setup) + end + + it "should return an http instance created with the passed host and port" do + http = stub 'http', :use_ssl= => nil, :read_timeout= => nil, :open_timeout= => nil, :started? => false + Net::HTTP.expects(:new).with("me", 54321, nil, nil).returns(http) + Puppet::Network::HttpPool.http_instance("me", 54321).should equal(http) + end + + it "should enable ssl on the http instance" do + Puppet::Network::HttpPool.http_instance("me", 54321).instance_variable_get("@use_ssl").should be_true + end + + it "should set the read timeout" do + Puppet::Network::HttpPool.http_instance("me", 54321).read_timeout.should == 120 + end + + it "should set the open timeout" do + Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 + end + + it "should create the http instance with the proxy host and port set if the http_proxy is not set to 'none'" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 + Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 + end + + describe "and http keep-alive is enabled" do + before do + Puppet::Network::HttpPool.stubs(:keep_alive?).returns true + end + + it "should cache http instances" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) + end + + it "should have a mechanism for getting a new http instance instead of the cached instance" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.http_instance("me", 54321, true).should_not equal(old) + end + + it "should close existing, open connections when requesting a new connection" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 + old = Puppet::Network::HttpPool.http_instance("me", 54321) + old.expects(:started?).returns(true) + old.expects(:finish) + Puppet::Network::HttpPool.http_instance("me", 54321, true) + end + + it "should have a mechanism for clearing the http cache" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.clear_http_instances + Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) + end + + it "should close open http connections when clearing the cache" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 + one = Puppet::Network::HttpPool.http_instance("me", 54321) + one.expects(:started?).returns(true) + one.expects(:finish).returns(true) + Puppet::Network::HttpPool.clear_http_instances + end + + it "should not close unopened http connections when clearing the cache" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 + one = Puppet::Network::HttpPool.http_instance("me", 54321) + one.expects(:started?).returns(false) + one.expects(:finish).never + Puppet::Network::HttpPool.clear_http_instances + end + end + + describe "and http keep-alive is disabled" do + before do + Puppet::Network::HttpPool.stubs(:keep_alive?).returns false + end + + it "should not cache http instances" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) + end + end + + after do + Puppet::Network::HttpPool.clear_http_instances + end + end + + describe "when adding certificate information to http instances" do + before do + @http = mock 'http' + [:cert_store=, :verify_mode=, :ca_file=, :cert=, :key=].each { |m| @http.stubs(m) } + @store = stub 'store' + + @cert = stub 'cert', :content => "real_cert" + @key = stub 'key', :content => "real_key" + @host = stub 'host', :certificate => @cert, :key => @key, :ssl_store => @store + + Puppet[:confdir] = "/sometthing/else" + Puppet.settings.stubs(:value).returns "/some/file" + Puppet.settings.stubs(:value).with(:hostcert).returns "/host/cert" + Puppet.settings.stubs(:value).with(:localcacert).returns "/local/ca/cert" + + FileTest.stubs(:exist?).with("/host/cert").returns true + FileTest.stubs(:exist?).with("/local/ca/cert").returns true + + Puppet::Network::HttpPool.stubs(:ssl_host).returns @host + end + + after do + Puppet.settings.clear + end + + it "should do nothing if no host certificate is on disk" do + FileTest.expects(:exist?).with("/host/cert").returns false + @http.expects(:cert=).never + Puppet::Network::HttpPool.cert_setup(@http) + end + + it "should do nothing if no local certificate is on disk" do + FileTest.expects(:exist?).with("/local/ca/cert").returns false + @http.expects(:cert=).never + Puppet::Network::HttpPool.cert_setup(@http) + end + + it "should add a certificate store from the ssl host" do + @http.expects(:cert_store=).with(@store) + + Puppet::Network::HttpPool.cert_setup(@http) + end + + it "should add the client certificate" do + @http.expects(:cert=).with("real_cert") + + Puppet::Network::HttpPool.cert_setup(@http) + end + + it "should add the client key" do + @http.expects(:key=).with("real_key") + + Puppet::Network::HttpPool.cert_setup(@http) + end + + it "should set the verify mode to OpenSSL::SSL::VERIFY_PEER" do + @http.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) + + Puppet::Network::HttpPool.cert_setup(@http) + end + + it "should set the ca file" do + Puppet.settings.stubs(:value).returns "/some/file" + FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns true + + Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert/file" + FileTest.stubs(:exist?).with("/ca/cert/file").returns true + @http.expects(:ca_file=).with("/ca/cert/file") + + Puppet::Network::HttpPool.cert_setup(@http) + end + + it "should set up certificate information when creating http instances" do + Puppet::Network::HttpPool.expects(:cert_setup).with { |i| i.is_a?(Net::HTTP) } + Puppet::Network::HttpPool.http_instance("one", "two") + end + end +end diff --git a/spec/unit/network/http_pool_spec_spec.rb b/spec/unit/network/http_pool_spec_spec.rb deleted file mode 100755 index 7fe55c5fa..000000000 --- a/spec/unit/network/http_pool_spec_spec.rb +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-11-26. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/network/http_pool' - -describe Puppet::Network::HttpPool do - after do - Puppet::Util::Cacher.expire - Puppet::Network::HttpPool.clear_http_instances - Puppet::Network::HttpPool.instance_variable_set("@ssl_host", nil) - end - - it "should have keep-alive disabled" do - Puppet::Network::HttpPool::HTTP_KEEP_ALIVE.should be_false - end - - it "should use the global SSL::Host instance to get its certificate information" do - host = mock 'host' - Puppet::SSL::Host.expects(:localhost).with().returns host - Puppet::Network::HttpPool.ssl_host.should equal(host) - end - - describe "when managing http instances" do - def stub_settings(settings) - settings.each do |param, value| - Puppet.settings.stubs(:value).with(param).returns(value) - end - end - - before do - # All of the cert stuff is tested elsewhere - Puppet::Network::HttpPool.stubs(:cert_setup) - end - - it "should return an http instance created with the passed host and port" do - http = stub 'http', :use_ssl= => nil, :read_timeout= => nil, :open_timeout= => nil, :started? => false - Net::HTTP.expects(:new).with("me", 54321, nil, nil).returns(http) - Puppet::Network::HttpPool.http_instance("me", 54321).should equal(http) - end - - it "should enable ssl on the http instance" do - Puppet::Network::HttpPool.http_instance("me", 54321).instance_variable_get("@use_ssl").should be_true - end - - it "should set the read timeout" do - Puppet::Network::HttpPool.http_instance("me", 54321).read_timeout.should == 120 - end - - it "should set the open timeout" do - Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 - end - - it "should create the http instance with the proxy host and port set if the http_proxy is not set to 'none'" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 - Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 - end - - describe "and http keep-alive is enabled" do - before do - Puppet::Network::HttpPool.stubs(:keep_alive?).returns true - end - - it "should cache http instances" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 - old = Puppet::Network::HttpPool.http_instance("me", 54321) - Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) - end - - it "should have a mechanism for getting a new http instance instead of the cached instance" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 - old = Puppet::Network::HttpPool.http_instance("me", 54321) - Puppet::Network::HttpPool.http_instance("me", 54321, true).should_not equal(old) - end - - it "should close existing, open connections when requesting a new connection" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 - old = Puppet::Network::HttpPool.http_instance("me", 54321) - old.expects(:started?).returns(true) - old.expects(:finish) - Puppet::Network::HttpPool.http_instance("me", 54321, true) - end - - it "should have a mechanism for clearing the http cache" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 - old = Puppet::Network::HttpPool.http_instance("me", 54321) - Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) - old = Puppet::Network::HttpPool.http_instance("me", 54321) - Puppet::Network::HttpPool.clear_http_instances - Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) - end - - it "should close open http connections when clearing the cache" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 - one = Puppet::Network::HttpPool.http_instance("me", 54321) - one.expects(:started?).returns(true) - one.expects(:finish).returns(true) - Puppet::Network::HttpPool.clear_http_instances - end - - it "should not close unopened http connections when clearing the cache" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 - one = Puppet::Network::HttpPool.http_instance("me", 54321) - one.expects(:started?).returns(false) - one.expects(:finish).never - Puppet::Network::HttpPool.clear_http_instances - end - end - - describe "and http keep-alive is disabled" do - before do - Puppet::Network::HttpPool.stubs(:keep_alive?).returns false - end - - it "should not cache http instances" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 - old = Puppet::Network::HttpPool.http_instance("me", 54321) - Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) - end - end - - after do - Puppet::Network::HttpPool.clear_http_instances - end - end - - describe "when adding certificate information to http instances" do - before do - @http = mock 'http' - [:cert_store=, :verify_mode=, :ca_file=, :cert=, :key=].each { |m| @http.stubs(m) } - @store = stub 'store' - - @cert = stub 'cert', :content => "real_cert" - @key = stub 'key', :content => "real_key" - @host = stub 'host', :certificate => @cert, :key => @key, :ssl_store => @store - - Puppet[:confdir] = "/sometthing/else" - Puppet.settings.stubs(:value).returns "/some/file" - Puppet.settings.stubs(:value).with(:hostcert).returns "/host/cert" - Puppet.settings.stubs(:value).with(:localcacert).returns "/local/ca/cert" - - FileTest.stubs(:exist?).with("/host/cert").returns true - FileTest.stubs(:exist?).with("/local/ca/cert").returns true - - Puppet::Network::HttpPool.stubs(:ssl_host).returns @host - end - - after do - Puppet.settings.clear - end - - it "should do nothing if no host certificate is on disk" do - FileTest.expects(:exist?).with("/host/cert").returns false - @http.expects(:cert=).never - Puppet::Network::HttpPool.cert_setup(@http) - end - - it "should do nothing if no local certificate is on disk" do - FileTest.expects(:exist?).with("/local/ca/cert").returns false - @http.expects(:cert=).never - Puppet::Network::HttpPool.cert_setup(@http) - end - - it "should add a certificate store from the ssl host" do - @http.expects(:cert_store=).with(@store) - - Puppet::Network::HttpPool.cert_setup(@http) - end - - it "should add the client certificate" do - @http.expects(:cert=).with("real_cert") - - Puppet::Network::HttpPool.cert_setup(@http) - end - - it "should add the client key" do - @http.expects(:key=).with("real_key") - - Puppet::Network::HttpPool.cert_setup(@http) - end - - it "should set the verify mode to OpenSSL::SSL::VERIFY_PEER" do - @http.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) - - Puppet::Network::HttpPool.cert_setup(@http) - end - - it "should set the ca file" do - Puppet.settings.stubs(:value).returns "/some/file" - FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns true - - Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert/file" - FileTest.stubs(:exist?).with("/ca/cert/file").returns true - @http.expects(:ca_file=).with("/ca/cert/file") - - Puppet::Network::HttpPool.cert_setup(@http) - end - - it "should set up certificate information when creating http instances" do - Puppet::Network::HttpPool.expects(:cert_setup).with { |i| i.is_a?(Net::HTTP) } - Puppet::Network::HttpPool.http_instance("one", "two") - end - end -end diff --git a/spec/unit/network/http_spec.rb b/spec/unit/network/http_spec.rb new file mode 100755 index 000000000..220726788 --- /dev/null +++ b/spec/unit/network/http_spec.rb @@ -0,0 +1,35 @@ +#!/usr/bin/env ruby +# +# Created by Rick Bradley on 2007-10-03. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/http' + +describe Puppet::Network::HTTP do + it "should return the webrick HTTP server class when asked for a webrick server" do + Puppet::Network::HTTP.server_class_by_type(:webrick).should be(Puppet::Network::HTTP::WEBrick) + end + + describe "when asked for a mongrel server" do + if Puppet.features.mongrel? + it "should return the mongrel server class" do + Puppet::Network::HTTP.server_class_by_type(:mongrel).should be(Puppet::Network::HTTP::Mongrel) + end + else + it "should fail" do + lambda { Puppet::Network::HTTP.server_class_by_type(:mongrel) }.should raise_error(ArgumentError) + end + end + end + + it "should fail to return the mongrel HTTP server class if mongrel is not available " do + Puppet.features.expects(:mongrel?).returns(false) + Proc.new { Puppet::Network::HTTP.server_class_by_type(:mongrel) }.should raise_error(ArgumentError) + end + + it "should return an error when asked for an unknown server" do + Proc.new { Puppet::Network::HTTP.server_class_by_type :foo }.should raise_error(ArgumentError) + end +end diff --git a/spec/unit/network/http_spec_spec.rb b/spec/unit/network/http_spec_spec.rb deleted file mode 100755 index 220726788..000000000 --- a/spec/unit/network/http_spec_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Rick Bradley on 2007-10-03. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/http' - -describe Puppet::Network::HTTP do - it "should return the webrick HTTP server class when asked for a webrick server" do - Puppet::Network::HTTP.server_class_by_type(:webrick).should be(Puppet::Network::HTTP::WEBrick) - end - - describe "when asked for a mongrel server" do - if Puppet.features.mongrel? - it "should return the mongrel server class" do - Puppet::Network::HTTP.server_class_by_type(:mongrel).should be(Puppet::Network::HTTP::Mongrel) - end - else - it "should fail" do - lambda { Puppet::Network::HTTP.server_class_by_type(:mongrel) }.should raise_error(ArgumentError) - end - end - end - - it "should fail to return the mongrel HTTP server class if mongrel is not available " do - Puppet.features.expects(:mongrel?).returns(false) - Proc.new { Puppet::Network::HTTP.server_class_by_type(:mongrel) }.should raise_error(ArgumentError) - end - - it "should return an error when asked for an unknown server" do - Proc.new { Puppet::Network::HTTP.server_class_by_type :foo }.should raise_error(ArgumentError) - end -end diff --git a/spec/unit/network/rest_authconfig_spec.rb b/spec/unit/network/rest_authconfig_spec.rb new file mode 100755 index 000000000..fe17d5626 --- /dev/null +++ b/spec/unit/network/rest_authconfig_spec.rb @@ -0,0 +1,148 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/rest_authconfig' + +describe Puppet::Network::RestAuthConfig do + + DEFAULT_ACL = [ + { :acl => "~ ^\/catalog\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true }, + # this one will allow all file access, and thus delegate + # to fileserver.conf + { :acl => "/file" }, + { :acl => "/certificate_revocation_list/ca", :method => :find, :authenticated => true }, + { :acl => "/report", :method => :save, :authenticated => true }, + { :acl => "/certificate/ca", :method => :find, :authenticated => false }, + { :acl => "/certificate/", :method => :find, :authenticated => false }, + { :acl => "/certificate_request", :method => [:find, :save], :authenticated => false }, + { :acl => "/status", :method => [:find], :authenticated => true }, + { :acl => "/resource", :method => [:find, :save, :search], :authenticated => true }, + ] + + before :each do + FileTest.stubs(:exists?).returns(true) + File.stubs(:stat).returns(stub('stat', :ctime => :now)) + Time.stubs(:now).returns :now + + @authconfig = Puppet::Network::RestAuthConfig.new("dummy", false) + @authconfig.stubs(:read) + + @acl = stub_everything 'rights' + @authconfig.rights = @acl + + @request = stub 'request', :indirection_name => "path", :key => "to/resource", :ip => "127.0.0.1", + :node => "me", :method => :save, :environment => :env, :authenticated => true + end + + it "should use the puppet default rest authorization file" do + Puppet.expects(:[]).with(:rest_authconfig).returns("dummy") + + Puppet::Network::RestAuthConfig.new(nil, false) + end + + it "should read the config file when needed" do + @authconfig.expects(:read) + + @authconfig.allowed?(@request) + end + + it "should ask for authorization to the ACL subsystem" do + @acl.expects(:fail_on_deny).with("/path/to/resource", :node => "me", :ip => "127.0.0.1", :method => :save, :environment => :env, :authenticated => true) + + @authconfig.allowed?(@request) + end + + describe "when defining an acl with mk_acl" do + it "should create a new right for each default acl" do + @acl.expects(:newright).with(:path) + @authconfig.mk_acl(:acl => :path) + end + + it "should allow everyone for each default right" do + @acl.expects(:allow).with(:path, "*") + @authconfig.mk_acl(:acl => :path) + end + + it "should restrict the ACL to a method" do + @acl.expects(:restrict_method).with(:path, :method) + @authconfig.mk_acl(:acl => :path, :method => :method) + end + + it "should restrict the ACL to a specific authentication state" do + @acl.expects(:restrict_authenticated).with(:path, :authentication) + @authconfig.mk_acl(:acl => :path, :authenticated => :authentication) + end + end + + describe "when parsing the configuration file" do + it "should check for missing ACL after reading the authconfig file" do + File.stubs(:open) + + @authconfig.expects(:insert_default_acl) + + @authconfig.parse() + end + end + + DEFAULT_ACL.each do |acl| + it "should insert #{acl[:acl]} if not present" do + @authconfig.rights.stubs(:[]).returns(true) + @authconfig.rights.stubs(:[]).with(acl[:acl]).returns(nil) + + @authconfig.expects(:mk_acl).with { |h| h[:acl] == acl[:acl] } + + @authconfig.insert_default_acl + end + + it "should not insert #{acl[:acl]} if present" do + @authconfig.rights.stubs(:[]).returns(true) + @authconfig.rights.stubs(:[]).with(acl).returns(true) + + @authconfig.expects(:mk_acl).never + + @authconfig.insert_default_acl + end + end + + it "should create default ACL entries if no file have been read" do + Puppet::Network::RestAuthConfig.any_instance.stubs(:exists?).returns(false) + + Puppet::Network::RestAuthConfig.any_instance.expects(:insert_default_acl) + + Puppet::Network::RestAuthConfig.main + end + + describe "when adding default ACLs" do + + DEFAULT_ACL.each do |acl| + it "should create a default right for #{acl[:acl]}" do + @authconfig.stubs(:mk_acl) + @authconfig.expects(:mk_acl).with(acl) + @authconfig.insert_default_acl + end + end + + it "should log at info loglevel" do + Puppet.expects(:info).at_least_once + @authconfig.insert_default_acl + end + + it "should create a last catch-all deny all rule" do + @authconfig.stubs(:mk_acl) + @acl.expects(:newright).with("/") + @authconfig.insert_default_acl + end + + it "should create a last catch-all deny all rule for any authenticated request state" do + @authconfig.stubs(:mk_acl) + @acl.stubs(:newright).with("/") + + @acl.expects(:restrict_authenticated).with("/", :any) + + @authconfig.insert_default_acl + end + + end + +end diff --git a/spec/unit/network/rest_authconfig_spec_spec.rb b/spec/unit/network/rest_authconfig_spec_spec.rb deleted file mode 100755 index fe17d5626..000000000 --- a/spec/unit/network/rest_authconfig_spec_spec.rb +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/rest_authconfig' - -describe Puppet::Network::RestAuthConfig do - - DEFAULT_ACL = [ - { :acl => "~ ^\/catalog\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true }, - # this one will allow all file access, and thus delegate - # to fileserver.conf - { :acl => "/file" }, - { :acl => "/certificate_revocation_list/ca", :method => :find, :authenticated => true }, - { :acl => "/report", :method => :save, :authenticated => true }, - { :acl => "/certificate/ca", :method => :find, :authenticated => false }, - { :acl => "/certificate/", :method => :find, :authenticated => false }, - { :acl => "/certificate_request", :method => [:find, :save], :authenticated => false }, - { :acl => "/status", :method => [:find], :authenticated => true }, - { :acl => "/resource", :method => [:find, :save, :search], :authenticated => true }, - ] - - before :each do - FileTest.stubs(:exists?).returns(true) - File.stubs(:stat).returns(stub('stat', :ctime => :now)) - Time.stubs(:now).returns :now - - @authconfig = Puppet::Network::RestAuthConfig.new("dummy", false) - @authconfig.stubs(:read) - - @acl = stub_everything 'rights' - @authconfig.rights = @acl - - @request = stub 'request', :indirection_name => "path", :key => "to/resource", :ip => "127.0.0.1", - :node => "me", :method => :save, :environment => :env, :authenticated => true - end - - it "should use the puppet default rest authorization file" do - Puppet.expects(:[]).with(:rest_authconfig).returns("dummy") - - Puppet::Network::RestAuthConfig.new(nil, false) - end - - it "should read the config file when needed" do - @authconfig.expects(:read) - - @authconfig.allowed?(@request) - end - - it "should ask for authorization to the ACL subsystem" do - @acl.expects(:fail_on_deny).with("/path/to/resource", :node => "me", :ip => "127.0.0.1", :method => :save, :environment => :env, :authenticated => true) - - @authconfig.allowed?(@request) - end - - describe "when defining an acl with mk_acl" do - it "should create a new right for each default acl" do - @acl.expects(:newright).with(:path) - @authconfig.mk_acl(:acl => :path) - end - - it "should allow everyone for each default right" do - @acl.expects(:allow).with(:path, "*") - @authconfig.mk_acl(:acl => :path) - end - - it "should restrict the ACL to a method" do - @acl.expects(:restrict_method).with(:path, :method) - @authconfig.mk_acl(:acl => :path, :method => :method) - end - - it "should restrict the ACL to a specific authentication state" do - @acl.expects(:restrict_authenticated).with(:path, :authentication) - @authconfig.mk_acl(:acl => :path, :authenticated => :authentication) - end - end - - describe "when parsing the configuration file" do - it "should check for missing ACL after reading the authconfig file" do - File.stubs(:open) - - @authconfig.expects(:insert_default_acl) - - @authconfig.parse() - end - end - - DEFAULT_ACL.each do |acl| - it "should insert #{acl[:acl]} if not present" do - @authconfig.rights.stubs(:[]).returns(true) - @authconfig.rights.stubs(:[]).with(acl[:acl]).returns(nil) - - @authconfig.expects(:mk_acl).with { |h| h[:acl] == acl[:acl] } - - @authconfig.insert_default_acl - end - - it "should not insert #{acl[:acl]} if present" do - @authconfig.rights.stubs(:[]).returns(true) - @authconfig.rights.stubs(:[]).with(acl).returns(true) - - @authconfig.expects(:mk_acl).never - - @authconfig.insert_default_acl - end - end - - it "should create default ACL entries if no file have been read" do - Puppet::Network::RestAuthConfig.any_instance.stubs(:exists?).returns(false) - - Puppet::Network::RestAuthConfig.any_instance.expects(:insert_default_acl) - - Puppet::Network::RestAuthConfig.main - end - - describe "when adding default ACLs" do - - DEFAULT_ACL.each do |acl| - it "should create a default right for #{acl[:acl]}" do - @authconfig.stubs(:mk_acl) - @authconfig.expects(:mk_acl).with(acl) - @authconfig.insert_default_acl - end - end - - it "should log at info loglevel" do - Puppet.expects(:info).at_least_once - @authconfig.insert_default_acl - end - - it "should create a last catch-all deny all rule" do - @authconfig.stubs(:mk_acl) - @acl.expects(:newright).with("/") - @authconfig.insert_default_acl - end - - it "should create a last catch-all deny all rule for any authenticated request state" do - @authconfig.stubs(:mk_acl) - @acl.stubs(:newright).with("/") - - @acl.expects(:restrict_authenticated).with("/", :any) - - @authconfig.insert_default_acl - end - - end - -end diff --git a/spec/unit/network/rest_authorization_spec.rb b/spec/unit/network/rest_authorization_spec.rb new file mode 100755 index 000000000..4703c181f --- /dev/null +++ b/spec/unit/network/rest_authorization_spec.rb @@ -0,0 +1,43 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/rest_authorization' + +class RestAuthorized + include Puppet::Network::RestAuthorization +end + + +describe Puppet::Network::RestAuthorization do + before :each do + @auth = RestAuthorized.new + @authconig = stub 'authconfig' + @auth.stubs(:authconfig).returns(@authconfig) + + @request = stub_everything 'request' + @request.stubs(:method).returns(:find) + @request.stubs(:node).returns("node") + @request.stubs(:ip).returns("ip") + end + + describe "when testing request authorization" do + it "should delegate to the current rest authconfig" do + @authconfig.expects(:allowed?).with(@request).returns(true) + + @auth.check_authorization(@request) + end + + it "should raise an AuthorizationError if authconfig raises an AuthorizationError" do + @authconfig.expects(:allowed?).with(@request).raises(Puppet::Network::AuthorizationError.new("forbidden")) + + lambda { @auth.check_authorization(@request) }.should raise_error(Puppet::Network::AuthorizationError) + end + + it "should not raise an AuthorizationError if request is allowed" do + @authconfig.expects(:allowed?).with(@request).returns(true) + + lambda { @auth.check_authorization(@request) }.should_not raise_error(Puppet::Network::AuthorizationError) + end + end +end diff --git a/spec/unit/network/rest_authorization_spec_spec.rb b/spec/unit/network/rest_authorization_spec_spec.rb deleted file mode 100755 index 4703c181f..000000000 --- a/spec/unit/network/rest_authorization_spec_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/rest_authorization' - -class RestAuthorized - include Puppet::Network::RestAuthorization -end - - -describe Puppet::Network::RestAuthorization do - before :each do - @auth = RestAuthorized.new - @authconig = stub 'authconfig' - @auth.stubs(:authconfig).returns(@authconfig) - - @request = stub_everything 'request' - @request.stubs(:method).returns(:find) - @request.stubs(:node).returns("node") - @request.stubs(:ip).returns("ip") - end - - describe "when testing request authorization" do - it "should delegate to the current rest authconfig" do - @authconfig.expects(:allowed?).with(@request).returns(true) - - @auth.check_authorization(@request) - end - - it "should raise an AuthorizationError if authconfig raises an AuthorizationError" do - @authconfig.expects(:allowed?).with(@request).raises(Puppet::Network::AuthorizationError.new("forbidden")) - - lambda { @auth.check_authorization(@request) }.should raise_error(Puppet::Network::AuthorizationError) - end - - it "should not raise an AuthorizationError if request is allowed" do - @authconfig.expects(:allowed?).with(@request).returns(true) - - lambda { @auth.check_authorization(@request) }.should_not raise_error(Puppet::Network::AuthorizationError) - end - end -end diff --git a/spec/unit/network/rights_spec.rb b/spec/unit/network/rights_spec.rb new file mode 100755 index 000000000..7f00891ac --- /dev/null +++ b/spec/unit/network/rights_spec.rb @@ -0,0 +1,519 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/rights' + +describe Puppet::Network::Rights do + before do + @right = Puppet::Network::Rights.new + end + + [:allow, :deny, :restrict_method, :restrict_environment, :restrict_authenticated].each do |m| + it "should have a #{m} method" do + @right.should respond_to(m) + end + + describe "when using #{m}" do + it "should delegate to the correct acl" do + acl = stub 'acl' + @right.stubs(:[]).returns(acl) + + acl.expects(m).with("me") + + @right.send(m, 'thisacl', "me") + end + end + end + + it "should throw an error if type can't be determined" do + lambda { @right.newright("name") }.should raise_error + end + + describe "when creating new namespace ACLs" do + + it "should throw an error if the ACL already exists" do + @right.newright("[name]") + + lambda { @right.newright("[name]") }.should raise_error + end + + it "should create a new ACL with the correct name" do + @right.newright("[name]") + + @right["name"].key.should == :name + end + + it "should create an ACL of type Puppet::Network::AuthStore" do + @right.newright("[name]") + + @right["name"].should be_a_kind_of(Puppet::Network::AuthStore) + end + end + + describe "when creating new path ACLs" do + it "should not throw an error if the ACL already exists" do + @right.newright("/name") + + lambda { @right.newright("/name")}.should_not raise_error + end + + it "should throw an error if the acl uri path is not absolute" do + lambda { @right.newright("name")}.should raise_error + end + + it "should create a new ACL with the correct path" do + @right.newright("/name") + + @right["/name"].should_not be_nil + end + + it "should create an ACL of type Puppet::Network::AuthStore" do + @right.newright("/name") + + @right["/name"].should be_a_kind_of(Puppet::Network::AuthStore) + end + end + + describe "when creating new regex ACLs" do + it "should not throw an error if the ACL already exists" do + @right.newright("~ .rb$") + + lambda { @right.newright("~ .rb$")}.should_not raise_error + end + + it "should create a new ACL with the correct regex" do + @right.newright("~ .rb$") + + @right.include?(".rb$").should_not be_nil + end + + it "should be able to lookup the regex" do + @right.newright("~ .rb$") + + @right[".rb$"].should_not be_nil + end + + it "should be able to lookup the regex by its full name" do + @right.newright("~ .rb$") + + @right["~ .rb$"].should_not be_nil + end + + it "should create an ACL of type Puppet::Network::AuthStore" do + @right.newright("~ .rb$").should be_a_kind_of(Puppet::Network::AuthStore) + end + end + + describe "when checking ACLs existence" do + it "should return false if there are no matching rights" do + @right.include?("name").should be_false + end + + it "should return true if a namespace rights exist" do + @right.newright("[name]") + + @right.include?("name").should be_true + end + + it "should return false if no matching namespace rights exist" do + @right.newright("[name]") + + @right.include?("notname").should be_false + end + + it "should return true if a path right exists" do + @right.newright("/name") + + @right.include?("/name").should be_true + end + + it "should return false if no matching path rights exist" do + @right.newright("/name") + + @right.include?("/differentname").should be_false + end + + it "should return true if a regex right exists" do + @right.newright("~ .rb$") + + @right.include?(".rb$").should be_true + end + + it "should return false if no matching path rights exist" do + @right.newright("~ .rb$") + + @right.include?(".pp$").should be_false + end + end + + describe "when checking if right is allowed" do + before :each do + @right.stubs(:right).returns(nil) + + @pathacl = stub 'pathacl', :acl_type => :regex, :"<=>" => 1, :line => 0, :file => 'dummy' + Puppet::Network::Rights::Right.stubs(:new).returns(@pathacl) + end + + it "should delegate to fail_on_deny" do + @right.expects(:fail_on_deny).with("namespace", :node => "host.domain.com", :ip => "127.0.0.1") + + @right.allowed?("namespace", "host.domain.com", "127.0.0.1") + end + + it "should return true if fail_on_deny doesn't fail" do + @right.stubs(:fail_on_deny) + @right.allowed?("namespace", :args).should be_true + end + + it "should return false if fail_on_deny raises an AuthorizationError" do + @right.stubs(:fail_on_deny).raises(Puppet::Network::AuthorizationError.new("forbidden")) + @right.allowed?("namespace", :args1, :args2).should be_false + end + + it "should first check namespace rights" do + acl = stub 'acl', :acl_type => :name, :key => :namespace + Puppet::Network::Rights::Right.stubs(:new).returns(acl) + + @right.newright("[namespace]") + acl.expects(:match?).returns(true) + acl.expects(:allowed?).with { |node,ip,h| node == "node" and ip == "ip" }.returns(true) + + @right.fail_on_deny("namespace", { :node => "node", :ip => "ip" } ) + end + + it "should then check for path rights if no namespace match" do + acl = stub 'nmacl', :acl_type => :name, :key => :namespace, :"<=>" => -1, :line => 0, :file => 'dummy' + acl.stubs(:match?).returns(false) + Puppet::Network::Rights::Right.stubs(:new).with("[namespace]").returns(acl) + + @right.newright("[namespace]") + @right.newright("/path/to/there", 0, nil) + + @pathacl.stubs(:match?).returns(true) + + acl.expects(:allowed?).never + @pathacl.expects(:allowed?).returns(true) + + @right.fail_on_deny("/path/to/there", {}) + end + + it "should pass the match? return to allowed?" do + @right.newright("/path/to/there") + + @pathacl.expects(:match?).returns(:match) + @pathacl.expects(:allowed?).with { |node,ip,h| h[:match] == :match }.returns(true) + + @right.fail_on_deny("/path/to/there", {}) + end + + describe "with namespace acls" do + it "should raise an error if this namespace right doesn't exist" do + lambda{ @right.fail_on_deny("namespace") }.should raise_error + end + end + + describe "with path acls" do + before :each do + @long_acl = stub 'longpathacl', :name => "/path/to/there", :acl_type => :regex, :line => 0, :file => 'dummy' + Puppet::Network::Rights::Right.stubs(:new).with("/path/to/there", 0, nil).returns(@long_acl) + + @short_acl = stub 'shortpathacl', :name => "/path/to", :acl_type => :regex, :line => 0, :file => 'dummy' + Puppet::Network::Rights::Right.stubs(:new).with("/path/to", 0, nil).returns(@short_acl) + + @long_acl.stubs(:"<=>").with(@short_acl).returns(0) + @short_acl.stubs(:"<=>").with(@long_acl).returns(0) + end + + it "should select the first match" do + @right.newright("/path/to/there", 0) + @right.newright("/path/to", 0) + + @long_acl.stubs(:match?).returns(true) + @short_acl.stubs(:match?).returns(true) + + @long_acl.expects(:allowed?).returns(true) + @short_acl.expects(:allowed?).never + + @right.fail_on_deny("/path/to/there/and/there", {}) + end + + it "should select the first match that doesn't return :dunno" do + @right.newright("/path/to/there", 0, nil) + @right.newright("/path/to", 0, nil) + + @long_acl.stubs(:match?).returns(true) + @short_acl.stubs(:match?).returns(true) + + @long_acl.expects(:allowed?).returns(:dunno) + @short_acl.expects(:allowed?).returns(true) + + @right.fail_on_deny("/path/to/there/and/there", {}) + end + + it "should not select an ACL that doesn't match" do + @right.newright("/path/to/there", 0) + @right.newright("/path/to", 0) + + @long_acl.stubs(:match?).returns(false) + @short_acl.stubs(:match?).returns(true) + + @long_acl.expects(:allowed?).never + @short_acl.expects(:allowed?).returns(true) + + @right.fail_on_deny("/path/to/there/and/there", {}) + end + + it "should not raise an AuthorizationError if allowed" do + @right.newright("/path/to/there", 0) + + @long_acl.stubs(:match?).returns(true) + @long_acl.stubs(:allowed?).returns(true) + + lambda { @right.fail_on_deny("/path/to/there/and/there", {}) }.should_not raise_error(Puppet::Network::AuthorizationError) + end + + it "should raise an AuthorizationError if the match is denied" do + @right.newright("/path/to/there", 0, nil) + + @long_acl.stubs(:match?).returns(true) + @long_acl.stubs(:allowed?).returns(false) + + lambda{ @right.fail_on_deny("/path/to/there", {}) }.should raise_error(Puppet::Network::AuthorizationError) + end + + it "should raise an AuthorizationError if no path match" do + lambda { @right.fail_on_deny("/nomatch", {}) }.should raise_error(Puppet::Network::AuthorizationError) + end + end + + describe "with regex acls" do + before :each do + @regex_acl1 = stub 'regex_acl1', :name => "/files/(.*)/myfile", :acl_type => :regex, :line => 0, :file => 'dummy' + Puppet::Network::Rights::Right.stubs(:new).with("~ /files/(.*)/myfile", 0, nil).returns(@regex_acl1) + + @regex_acl2 = stub 'regex_acl2', :name => "/files/(.*)/myfile/", :acl_type => :regex, :line => 0, :file => 'dummy' + Puppet::Network::Rights::Right.stubs(:new).with("~ /files/(.*)/myfile/", 0, nil).returns(@regex_acl2) + + @regex_acl1.stubs(:"<=>").with(@regex_acl2).returns(0) + @regex_acl2.stubs(:"<=>").with(@regex_acl1).returns(0) + end + + it "should select the first match" do + @right.newright("~ /files/(.*)/myfile", 0) + @right.newright("~ /files/(.*)/myfile/", 0) + + @regex_acl1.stubs(:match?).returns(true) + @regex_acl2.stubs(:match?).returns(true) + + @regex_acl1.expects(:allowed?).returns(true) + @regex_acl2.expects(:allowed?).never + + @right.fail_on_deny("/files/repository/myfile/other", {}) + end + + it "should select the first match that doesn't return :dunno" do + @right.newright("~ /files/(.*)/myfile", 0) + @right.newright("~ /files/(.*)/myfile/", 0) + + @regex_acl1.stubs(:match?).returns(true) + @regex_acl2.stubs(:match?).returns(true) + + @regex_acl1.expects(:allowed?).returns(:dunno) + @regex_acl2.expects(:allowed?).returns(true) + + @right.fail_on_deny("/files/repository/myfile/other", {}) + end + + it "should not select an ACL that doesn't match" do + @right.newright("~ /files/(.*)/myfile", 0) + @right.newright("~ /files/(.*)/myfile/", 0) + + @regex_acl1.stubs(:match?).returns(false) + @regex_acl2.stubs(:match?).returns(true) + + @regex_acl1.expects(:allowed?).never + @regex_acl2.expects(:allowed?).returns(true) + + @right.fail_on_deny("/files/repository/myfile/other", {}) + end + + it "should not raise an AuthorizationError if allowed" do + @right.newright("~ /files/(.*)/myfile", 0) + + @regex_acl1.stubs(:match?).returns(true) + @regex_acl1.stubs(:allowed?).returns(true) + + lambda { @right.fail_on_deny("/files/repository/myfile/other", {}) }.should_not raise_error(Puppet::Network::AuthorizationError) + end + + it "should raise an error if no regex acl match" do + lambda{ @right.fail_on_deny("/path", {}) }.should raise_error(Puppet::Network::AuthorizationError) + end + + it "should raise an AuthorizedError on deny" do + lambda { @right.fail_on_deny("/path", {}) }.should raise_error(Puppet::Network::AuthorizationError) + end + + end + end + + describe Puppet::Network::Rights::Right do + before :each do + @acl = Puppet::Network::Rights::Right.new("/path",0, nil) + end + + describe "with path" do + it "should say it's a regex ACL" do + @acl.acl_type.should == :regex + end + + it "should match up to its path length" do + @acl.match?("/path/that/works").should_not be_nil + end + + it "should match up to its path length" do + @acl.match?("/paththatalsoworks").should_not be_nil + end + + it "should return nil if no match" do + @acl.match?("/notpath").should be_nil + end + end + + describe "with regex" do + before :each do + @acl = Puppet::Network::Rights::Right.new("~ .rb$",0, nil) + end + + it "should say it's a regex ACL" do + @acl.acl_type.should == :regex + end + + it "should match as a regex" do + @acl.match?("this should work.rb").should_not be_nil + end + + it "should return nil if no match" do + @acl.match?("do not match").should be_nil + end + end + + it "should allow all rest methods by default" do + @acl.methods.should == Puppet::Network::Rights::Right::ALL + end + + it "should allow only authenticated request by default" do + @acl.authentication.should be_true + end + + it "should allow modification of the methods filters" do + @acl.restrict_method(:save) + + @acl.methods.should == [:save] + end + + it "should stack methods filters" do + @acl.restrict_method(:save) + @acl.restrict_method(:destroy) + + @acl.methods.should == [:save, :destroy] + end + + it "should raise an error if the method is already filtered" do + @acl.restrict_method(:save) + + lambda { @acl.restrict_method(:save) }.should raise_error + end + + it "should allow setting an environment filters" do + Puppet::Node::Environment.stubs(:new).with(:environment).returns(:env) + + @acl.restrict_environment(:environment) + + @acl.environment.should == [:env] + end + + ["on", "yes", "true", true].each do |auth| + it "should allow filtering on authenticated requests with '#{auth}'" do + @acl.restrict_authenticated(auth) + + @acl.authentication.should be_true + end + end + + ["off", "no", "false", false].each do |auth| + it "should allow filtering on unauthenticated requests with '#{auth}'" do + @acl.restrict_authenticated(auth) + + @acl.authentication.should be_false + end + end + + ["all", "any", :all, :any].each do |auth| + it "should not use request authenticated state filtering with '#{auth}'" do + @acl.restrict_authenticated(auth) + + @acl.authentication.should be_nil + end + end + + describe "when checking right authorization" do + it "should return :dunno if this right is not restricted to the given method" do + @acl.restrict_method(:destroy) + + @acl.allowed?("me","127.0.0.1", { :method => :save } ).should == :dunno + end + + it "should return allow/deny if this right is restricted to the given method" do + @acl.restrict_method(:save) + @acl.allow("127.0.0.1") + + @acl.allowed?("me","127.0.0.1", { :method => :save }).should be_true + end + + it "should return :dunno if this right is not restricted to the given environment" do + Puppet::Node::Environment.stubs(:new).returns(:production) + + @acl.restrict_environment(:production) + + @acl.allowed?("me","127.0.0.1", { :method => :save, :environment => :development }).should == :dunno + end + + it "should return :dunno if this right is not restricted to the given request authentication state" do + @acl.restrict_authenticated(true) + + @acl.allowed?("me","127.0.0.1", { :method => :save, :authenticated => false }).should == :dunno + end + + it "should return allow/deny if this right is restricted to the given request authentication state" do + @acl.restrict_authenticated(false) + @acl.allow("127.0.0.1") + + @acl.allowed?("me","127.0.0.1", { :authenticated => false }).should be_true + end + + it "should interpolate allow/deny patterns with the given match" do + @acl.expects(:interpolate).with(:match) + + @acl.allowed?("me","127.0.0.1", { :method => :save, :match => :match, :authenticated => true }) + end + + it "should reset interpolation after the match" do + @acl.expects(:reset_interpolation) + + @acl.allowed?("me","127.0.0.1", { :method => :save, :match => :match, :authenticated => true }) + end + + # mocha doesn't allow testing super... + # it "should delegate to the AuthStore for the result" do + # @acl.method(:save) + # + # @acl.expects(:allowed?).with("me","127.0.0.1") + # + # @acl.allowed?("me","127.0.0.1", :save) + # end + end + end + +end diff --git a/spec/unit/network/rights_spec_spec.rb b/spec/unit/network/rights_spec_spec.rb deleted file mode 100755 index 7f00891ac..000000000 --- a/spec/unit/network/rights_spec_spec.rb +++ /dev/null @@ -1,519 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/rights' - -describe Puppet::Network::Rights do - before do - @right = Puppet::Network::Rights.new - end - - [:allow, :deny, :restrict_method, :restrict_environment, :restrict_authenticated].each do |m| - it "should have a #{m} method" do - @right.should respond_to(m) - end - - describe "when using #{m}" do - it "should delegate to the correct acl" do - acl = stub 'acl' - @right.stubs(:[]).returns(acl) - - acl.expects(m).with("me") - - @right.send(m, 'thisacl', "me") - end - end - end - - it "should throw an error if type can't be determined" do - lambda { @right.newright("name") }.should raise_error - end - - describe "when creating new namespace ACLs" do - - it "should throw an error if the ACL already exists" do - @right.newright("[name]") - - lambda { @right.newright("[name]") }.should raise_error - end - - it "should create a new ACL with the correct name" do - @right.newright("[name]") - - @right["name"].key.should == :name - end - - it "should create an ACL of type Puppet::Network::AuthStore" do - @right.newright("[name]") - - @right["name"].should be_a_kind_of(Puppet::Network::AuthStore) - end - end - - describe "when creating new path ACLs" do - it "should not throw an error if the ACL already exists" do - @right.newright("/name") - - lambda { @right.newright("/name")}.should_not raise_error - end - - it "should throw an error if the acl uri path is not absolute" do - lambda { @right.newright("name")}.should raise_error - end - - it "should create a new ACL with the correct path" do - @right.newright("/name") - - @right["/name"].should_not be_nil - end - - it "should create an ACL of type Puppet::Network::AuthStore" do - @right.newright("/name") - - @right["/name"].should be_a_kind_of(Puppet::Network::AuthStore) - end - end - - describe "when creating new regex ACLs" do - it "should not throw an error if the ACL already exists" do - @right.newright("~ .rb$") - - lambda { @right.newright("~ .rb$")}.should_not raise_error - end - - it "should create a new ACL with the correct regex" do - @right.newright("~ .rb$") - - @right.include?(".rb$").should_not be_nil - end - - it "should be able to lookup the regex" do - @right.newright("~ .rb$") - - @right[".rb$"].should_not be_nil - end - - it "should be able to lookup the regex by its full name" do - @right.newright("~ .rb$") - - @right["~ .rb$"].should_not be_nil - end - - it "should create an ACL of type Puppet::Network::AuthStore" do - @right.newright("~ .rb$").should be_a_kind_of(Puppet::Network::AuthStore) - end - end - - describe "when checking ACLs existence" do - it "should return false if there are no matching rights" do - @right.include?("name").should be_false - end - - it "should return true if a namespace rights exist" do - @right.newright("[name]") - - @right.include?("name").should be_true - end - - it "should return false if no matching namespace rights exist" do - @right.newright("[name]") - - @right.include?("notname").should be_false - end - - it "should return true if a path right exists" do - @right.newright("/name") - - @right.include?("/name").should be_true - end - - it "should return false if no matching path rights exist" do - @right.newright("/name") - - @right.include?("/differentname").should be_false - end - - it "should return true if a regex right exists" do - @right.newright("~ .rb$") - - @right.include?(".rb$").should be_true - end - - it "should return false if no matching path rights exist" do - @right.newright("~ .rb$") - - @right.include?(".pp$").should be_false - end - end - - describe "when checking if right is allowed" do - before :each do - @right.stubs(:right).returns(nil) - - @pathacl = stub 'pathacl', :acl_type => :regex, :"<=>" => 1, :line => 0, :file => 'dummy' - Puppet::Network::Rights::Right.stubs(:new).returns(@pathacl) - end - - it "should delegate to fail_on_deny" do - @right.expects(:fail_on_deny).with("namespace", :node => "host.domain.com", :ip => "127.0.0.1") - - @right.allowed?("namespace", "host.domain.com", "127.0.0.1") - end - - it "should return true if fail_on_deny doesn't fail" do - @right.stubs(:fail_on_deny) - @right.allowed?("namespace", :args).should be_true - end - - it "should return false if fail_on_deny raises an AuthorizationError" do - @right.stubs(:fail_on_deny).raises(Puppet::Network::AuthorizationError.new("forbidden")) - @right.allowed?("namespace", :args1, :args2).should be_false - end - - it "should first check namespace rights" do - acl = stub 'acl', :acl_type => :name, :key => :namespace - Puppet::Network::Rights::Right.stubs(:new).returns(acl) - - @right.newright("[namespace]") - acl.expects(:match?).returns(true) - acl.expects(:allowed?).with { |node,ip,h| node == "node" and ip == "ip" }.returns(true) - - @right.fail_on_deny("namespace", { :node => "node", :ip => "ip" } ) - end - - it "should then check for path rights if no namespace match" do - acl = stub 'nmacl', :acl_type => :name, :key => :namespace, :"<=>" => -1, :line => 0, :file => 'dummy' - acl.stubs(:match?).returns(false) - Puppet::Network::Rights::Right.stubs(:new).with("[namespace]").returns(acl) - - @right.newright("[namespace]") - @right.newright("/path/to/there", 0, nil) - - @pathacl.stubs(:match?).returns(true) - - acl.expects(:allowed?).never - @pathacl.expects(:allowed?).returns(true) - - @right.fail_on_deny("/path/to/there", {}) - end - - it "should pass the match? return to allowed?" do - @right.newright("/path/to/there") - - @pathacl.expects(:match?).returns(:match) - @pathacl.expects(:allowed?).with { |node,ip,h| h[:match] == :match }.returns(true) - - @right.fail_on_deny("/path/to/there", {}) - end - - describe "with namespace acls" do - it "should raise an error if this namespace right doesn't exist" do - lambda{ @right.fail_on_deny("namespace") }.should raise_error - end - end - - describe "with path acls" do - before :each do - @long_acl = stub 'longpathacl', :name => "/path/to/there", :acl_type => :regex, :line => 0, :file => 'dummy' - Puppet::Network::Rights::Right.stubs(:new).with("/path/to/there", 0, nil).returns(@long_acl) - - @short_acl = stub 'shortpathacl', :name => "/path/to", :acl_type => :regex, :line => 0, :file => 'dummy' - Puppet::Network::Rights::Right.stubs(:new).with("/path/to", 0, nil).returns(@short_acl) - - @long_acl.stubs(:"<=>").with(@short_acl).returns(0) - @short_acl.stubs(:"<=>").with(@long_acl).returns(0) - end - - it "should select the first match" do - @right.newright("/path/to/there", 0) - @right.newright("/path/to", 0) - - @long_acl.stubs(:match?).returns(true) - @short_acl.stubs(:match?).returns(true) - - @long_acl.expects(:allowed?).returns(true) - @short_acl.expects(:allowed?).never - - @right.fail_on_deny("/path/to/there/and/there", {}) - end - - it "should select the first match that doesn't return :dunno" do - @right.newright("/path/to/there", 0, nil) - @right.newright("/path/to", 0, nil) - - @long_acl.stubs(:match?).returns(true) - @short_acl.stubs(:match?).returns(true) - - @long_acl.expects(:allowed?).returns(:dunno) - @short_acl.expects(:allowed?).returns(true) - - @right.fail_on_deny("/path/to/there/and/there", {}) - end - - it "should not select an ACL that doesn't match" do - @right.newright("/path/to/there", 0) - @right.newright("/path/to", 0) - - @long_acl.stubs(:match?).returns(false) - @short_acl.stubs(:match?).returns(true) - - @long_acl.expects(:allowed?).never - @short_acl.expects(:allowed?).returns(true) - - @right.fail_on_deny("/path/to/there/and/there", {}) - end - - it "should not raise an AuthorizationError if allowed" do - @right.newright("/path/to/there", 0) - - @long_acl.stubs(:match?).returns(true) - @long_acl.stubs(:allowed?).returns(true) - - lambda { @right.fail_on_deny("/path/to/there/and/there", {}) }.should_not raise_error(Puppet::Network::AuthorizationError) - end - - it "should raise an AuthorizationError if the match is denied" do - @right.newright("/path/to/there", 0, nil) - - @long_acl.stubs(:match?).returns(true) - @long_acl.stubs(:allowed?).returns(false) - - lambda{ @right.fail_on_deny("/path/to/there", {}) }.should raise_error(Puppet::Network::AuthorizationError) - end - - it "should raise an AuthorizationError if no path match" do - lambda { @right.fail_on_deny("/nomatch", {}) }.should raise_error(Puppet::Network::AuthorizationError) - end - end - - describe "with regex acls" do - before :each do - @regex_acl1 = stub 'regex_acl1', :name => "/files/(.*)/myfile", :acl_type => :regex, :line => 0, :file => 'dummy' - Puppet::Network::Rights::Right.stubs(:new).with("~ /files/(.*)/myfile", 0, nil).returns(@regex_acl1) - - @regex_acl2 = stub 'regex_acl2', :name => "/files/(.*)/myfile/", :acl_type => :regex, :line => 0, :file => 'dummy' - Puppet::Network::Rights::Right.stubs(:new).with("~ /files/(.*)/myfile/", 0, nil).returns(@regex_acl2) - - @regex_acl1.stubs(:"<=>").with(@regex_acl2).returns(0) - @regex_acl2.stubs(:"<=>").with(@regex_acl1).returns(0) - end - - it "should select the first match" do - @right.newright("~ /files/(.*)/myfile", 0) - @right.newright("~ /files/(.*)/myfile/", 0) - - @regex_acl1.stubs(:match?).returns(true) - @regex_acl2.stubs(:match?).returns(true) - - @regex_acl1.expects(:allowed?).returns(true) - @regex_acl2.expects(:allowed?).never - - @right.fail_on_deny("/files/repository/myfile/other", {}) - end - - it "should select the first match that doesn't return :dunno" do - @right.newright("~ /files/(.*)/myfile", 0) - @right.newright("~ /files/(.*)/myfile/", 0) - - @regex_acl1.stubs(:match?).returns(true) - @regex_acl2.stubs(:match?).returns(true) - - @regex_acl1.expects(:allowed?).returns(:dunno) - @regex_acl2.expects(:allowed?).returns(true) - - @right.fail_on_deny("/files/repository/myfile/other", {}) - end - - it "should not select an ACL that doesn't match" do - @right.newright("~ /files/(.*)/myfile", 0) - @right.newright("~ /files/(.*)/myfile/", 0) - - @regex_acl1.stubs(:match?).returns(false) - @regex_acl2.stubs(:match?).returns(true) - - @regex_acl1.expects(:allowed?).never - @regex_acl2.expects(:allowed?).returns(true) - - @right.fail_on_deny("/files/repository/myfile/other", {}) - end - - it "should not raise an AuthorizationError if allowed" do - @right.newright("~ /files/(.*)/myfile", 0) - - @regex_acl1.stubs(:match?).returns(true) - @regex_acl1.stubs(:allowed?).returns(true) - - lambda { @right.fail_on_deny("/files/repository/myfile/other", {}) }.should_not raise_error(Puppet::Network::AuthorizationError) - end - - it "should raise an error if no regex acl match" do - lambda{ @right.fail_on_deny("/path", {}) }.should raise_error(Puppet::Network::AuthorizationError) - end - - it "should raise an AuthorizedError on deny" do - lambda { @right.fail_on_deny("/path", {}) }.should raise_error(Puppet::Network::AuthorizationError) - end - - end - end - - describe Puppet::Network::Rights::Right do - before :each do - @acl = Puppet::Network::Rights::Right.new("/path",0, nil) - end - - describe "with path" do - it "should say it's a regex ACL" do - @acl.acl_type.should == :regex - end - - it "should match up to its path length" do - @acl.match?("/path/that/works").should_not be_nil - end - - it "should match up to its path length" do - @acl.match?("/paththatalsoworks").should_not be_nil - end - - it "should return nil if no match" do - @acl.match?("/notpath").should be_nil - end - end - - describe "with regex" do - before :each do - @acl = Puppet::Network::Rights::Right.new("~ .rb$",0, nil) - end - - it "should say it's a regex ACL" do - @acl.acl_type.should == :regex - end - - it "should match as a regex" do - @acl.match?("this should work.rb").should_not be_nil - end - - it "should return nil if no match" do - @acl.match?("do not match").should be_nil - end - end - - it "should allow all rest methods by default" do - @acl.methods.should == Puppet::Network::Rights::Right::ALL - end - - it "should allow only authenticated request by default" do - @acl.authentication.should be_true - end - - it "should allow modification of the methods filters" do - @acl.restrict_method(:save) - - @acl.methods.should == [:save] - end - - it "should stack methods filters" do - @acl.restrict_method(:save) - @acl.restrict_method(:destroy) - - @acl.methods.should == [:save, :destroy] - end - - it "should raise an error if the method is already filtered" do - @acl.restrict_method(:save) - - lambda { @acl.restrict_method(:save) }.should raise_error - end - - it "should allow setting an environment filters" do - Puppet::Node::Environment.stubs(:new).with(:environment).returns(:env) - - @acl.restrict_environment(:environment) - - @acl.environment.should == [:env] - end - - ["on", "yes", "true", true].each do |auth| - it "should allow filtering on authenticated requests with '#{auth}'" do - @acl.restrict_authenticated(auth) - - @acl.authentication.should be_true - end - end - - ["off", "no", "false", false].each do |auth| - it "should allow filtering on unauthenticated requests with '#{auth}'" do - @acl.restrict_authenticated(auth) - - @acl.authentication.should be_false - end - end - - ["all", "any", :all, :any].each do |auth| - it "should not use request authenticated state filtering with '#{auth}'" do - @acl.restrict_authenticated(auth) - - @acl.authentication.should be_nil - end - end - - describe "when checking right authorization" do - it "should return :dunno if this right is not restricted to the given method" do - @acl.restrict_method(:destroy) - - @acl.allowed?("me","127.0.0.1", { :method => :save } ).should == :dunno - end - - it "should return allow/deny if this right is restricted to the given method" do - @acl.restrict_method(:save) - @acl.allow("127.0.0.1") - - @acl.allowed?("me","127.0.0.1", { :method => :save }).should be_true - end - - it "should return :dunno if this right is not restricted to the given environment" do - Puppet::Node::Environment.stubs(:new).returns(:production) - - @acl.restrict_environment(:production) - - @acl.allowed?("me","127.0.0.1", { :method => :save, :environment => :development }).should == :dunno - end - - it "should return :dunno if this right is not restricted to the given request authentication state" do - @acl.restrict_authenticated(true) - - @acl.allowed?("me","127.0.0.1", { :method => :save, :authenticated => false }).should == :dunno - end - - it "should return allow/deny if this right is restricted to the given request authentication state" do - @acl.restrict_authenticated(false) - @acl.allow("127.0.0.1") - - @acl.allowed?("me","127.0.0.1", { :authenticated => false }).should be_true - end - - it "should interpolate allow/deny patterns with the given match" do - @acl.expects(:interpolate).with(:match) - - @acl.allowed?("me","127.0.0.1", { :method => :save, :match => :match, :authenticated => true }) - end - - it "should reset interpolation after the match" do - @acl.expects(:reset_interpolation) - - @acl.allowed?("me","127.0.0.1", { :method => :save, :match => :match, :authenticated => true }) - end - - # mocha doesn't allow testing super... - # it "should delegate to the AuthStore for the result" do - # @acl.method(:save) - # - # @acl.expects(:allowed?).with("me","127.0.0.1") - # - # @acl.allowed?("me","127.0.0.1", :save) - # end - end - end - -end diff --git a/spec/unit/network/server_spec.rb b/spec/unit/network/server_spec.rb new file mode 100755 index 000000000..2aeb99a8b --- /dev/null +++ b/spec/unit/network/server_spec.rb @@ -0,0 +1,534 @@ +#!/usr/bin/env ruby +# +# Created by Rick Bradley on 2007-10-03. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/network/server' + +describe Puppet::Network::Server do + before do + @mock_http_server_class = mock('http server class') + Puppet.settings.stubs(:use) + Puppet.settings.stubs(:value).with(:name).returns("me") + Puppet.settings.stubs(:value).with(:servertype).returns(:suparserver) + Puppet.settings.stubs(:value).with(:bindaddress).returns("") + Puppet.settings.stubs(:value).with(:masterport).returns(8140) + Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) + Puppet.settings.stubs(:value).with(:servertype).returns(:suparserver) + @server = Puppet::Network::Server.new(:port => 31337) + end + + describe "when initializing" do + before do + Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') + Puppet::Network::Handler.stubs(:handler).returns mock('xmlrpc_handler') + Puppet.settings.stubs(:value).with(:bindaddress).returns("") + Puppet.settings.stubs(:value).with(:masterport).returns('') + end + + it 'should fail if an unknown option is provided' do + lambda { Puppet::Network::Server.new(:foo => 31337) }.should raise_error(ArgumentError) + end + + it "should allow specifying a listening port" do + Puppet.settings.stubs(:value).with(:bindaddress).returns('') + @server = Puppet::Network::Server.new(:port => 31337) + @server.port.should == 31337 + end + + it "should use the :bindaddress setting to determine the default listening address" do + Puppet.settings.stubs(:value).with(:masterport).returns('') + Puppet.settings.expects(:value).with(:bindaddress).returns("10.0.0.1") + @server = Puppet::Network::Server.new + @server.address.should == "10.0.0.1" + end + + it "should set the bind address to '127.0.0.1' if the default address is an empty string and the server type is mongrel" do + Puppet.settings.stubs(:value).with(:servertype).returns("mongrel") + Puppet.settings.expects(:value).with(:bindaddress).returns("") + @server = Puppet::Network::Server.new + @server.address.should == '127.0.0.1' + end + + it "should set the bind address to '0.0.0.0' if the default address is an empty string and the server type is webrick" do + Puppet.settings.stubs(:value).with(:servertype).returns("webrick") + Puppet.settings.expects(:value).with(:bindaddress).returns("") + @server = Puppet::Network::Server.new + @server.address.should == '0.0.0.0' + end + + it "should use the Puppet configurator to find a default listening port" do + Puppet.settings.stubs(:value).with(:bindaddress).returns('') + Puppet.settings.expects(:value).with(:masterport).returns(6667) + @server = Puppet::Network::Server.new + @server.port.should == 6667 + end + + it "should fail to initialize if no listening port can be found" do + Puppet.settings.stubs(:value).with(:bindaddress).returns("127.0.0.1") + Puppet.settings.stubs(:value).with(:masterport).returns(nil) + lambda { Puppet::Network::Server.new }.should raise_error(ArgumentError) + end + + it "should use the Puppet configurator to determine which HTTP server will be used to provide access to clients" do + Puppet.settings.expects(:value).with(:servertype).returns(:suparserver) + @server = Puppet::Network::Server.new(:port => 31337) + @server.server_type.should == :suparserver + end + + it "should fail to initialize if there is no HTTP server known to the Puppet configurator" do + Puppet.settings.expects(:value).with(:servertype).returns(nil) + lambda { Puppet::Network::Server.new(:port => 31337) }.should raise_error + end + + it "should ask the Puppet::Network::HTTP class to fetch the proper HTTP server class" do + Puppet::Network::HTTP.expects(:server_class_by_type).with(:suparserver).returns(@mock_http_server_class) + @server = Puppet::Network::Server.new(:port => 31337) + end + + it "should fail if the HTTP server class is unknown" do + Puppet::Network::HTTP.stubs(:server_class_by_type).returns(nil) + lambda { Puppet::Network::Server.new(:port => 31337) }.should raise_error(ArgumentError) + end + + it "should allow registering REST handlers" do + @server = Puppet::Network::Server.new(:port => 31337, :handlers => [ :foo, :bar, :baz]) + lambda { @server.unregister(:foo, :bar, :baz) }.should_not raise_error + end + + it "should allow registering XMLRPC handlers" do + @server = Puppet::Network::Server.new(:port => 31337, :xmlrpc_handlers => [ :foo, :bar, :baz]) + lambda { @server.unregister_xmlrpc(:foo, :bar, :baz) }.should_not raise_error + end + + it "should not be listening after initialization" do + Puppet::Network::Server.new(:port => 31337).should_not be_listening + end + + it "should use the :main setting section" do + Puppet.settings.expects(:use).with { |*args| args.include?(:main) } + @server = Puppet::Network::Server.new(:port => 31337, :xmlrpc_handlers => [ :foo, :bar, :baz]) + end + + it "should use the Puppet[:name] setting section" do + Puppet.settings.expects(:value).with(:name).returns "me" + Puppet.settings.expects(:use).with { |*args| args.include?("me") } + + @server = Puppet::Network::Server.new(:port => 31337, :xmlrpc_handlers => [ :foo, :bar, :baz]) + end + end + + # We don't test the method, because it's too much of a Unix-y pain. + it "should be able to daemonize" do + @server.should respond_to(:daemonize) + end + + describe "when being started" do + before do + @server.stubs(:listen) + @server.stubs(:create_pidfile) + end + + it "should listen" do + @server.expects(:listen) + @server.start + end + + it "should create its PID file" do + @server.expects(:create_pidfile) + @server.start + end + end + + describe "when being stopped" do + before do + @server.stubs(:unlisten) + @server.stubs(:remove_pidfile) + end + + it "should unlisten" do + @server.expects(:unlisten) + @server.stop + end + + it "should remove its PID file" do + @server.expects(:remove_pidfile) + @server.stop + end + end + + describe "when creating its pidfile" do + it "should use an exclusive mutex" do + Puppet.settings.expects(:value).with(:name).returns "me" + + sync = mock 'sync' + Puppet::Util.expects(:sync).with("me").returns sync + + sync.expects(:synchronize).with(Sync::EX) + @server.create_pidfile + end + + it "should lock the pidfile using the Pidlock class" do + pidfile = mock 'pidfile' + + Puppet.settings.stubs(:value).with(:name).returns "eh" + Puppet.settings.expects(:value).with(:pidfile).returns "/my/file" + + Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile + + pidfile.expects(:lock).returns true + @server.create_pidfile + end + + it "should fail if it cannot lock" do + pidfile = mock 'pidfile' + + Puppet.settings.stubs(:value).with(:name).returns "eh" + Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" + + Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile + + pidfile.expects(:lock).returns false + + lambda { @server.create_pidfile }.should raise_error + end + end + + describe "when removing its pidfile" do + it "should use an exclusive mutex" do + Puppet.settings.expects(:value).with(:name).returns "me" + + sync = mock 'sync' + Puppet::Util.expects(:sync).with("me").returns sync + + sync.expects(:synchronize).with(Sync::EX) + @server.remove_pidfile + end + + it "should do nothing if the pidfile is not present" do + pidfile = mock 'pidfile', :locked? => false + Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile + + Puppet.settings.stubs(:value).with(:name).returns "eh" + Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" + + pidfile.expects(:unlock).never + @server.remove_pidfile + end + + it "should unlock the pidfile using the Pidlock class" do + pidfile = mock 'pidfile', :locked? => true + Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile + pidfile.expects(:unlock).returns true + + Puppet.settings.stubs(:value).with(:name).returns "eh" + Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" + + @server.remove_pidfile + end + + it "should warn if it cannot remove the pidfile" do + pidfile = mock 'pidfile', :locked? => true + Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile + pidfile.expects(:unlock).returns false + + Puppet.settings.stubs(:value).with(:name).returns "eh" + Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" + + Puppet.expects :err + @server.remove_pidfile + end + end + + describe "when managing indirection registrations" do + before do + Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') + end + + it "should allow registering an indirection for client access by specifying its indirection name" do + lambda { @server.register(:foo) }.should_not raise_error + end + + it "should require that the indirection be valid" do + Puppet::Indirector::Indirection.expects(:model).with(:foo).returns nil + lambda { @server.register(:foo) }.should raise_error(ArgumentError) + end + + it "should require at least one indirection name when registering indirections for client access" do + lambda { @server.register }.should raise_error(ArgumentError) + end + + it "should allow for numerous indirections to be registered at once for client access" do + lambda { @server.register(:foo, :bar, :baz) }.should_not raise_error + end + + it "should allow the use of indirection names to specify which indirections are to be no longer accessible to clients" do + @server.register(:foo) + lambda { @server.unregister(:foo) }.should_not raise_error + end + + it "should leave other indirections accessible to clients when turning off indirections" do + @server.register(:foo, :bar) + @server.unregister(:foo) + lambda { @server.unregister(:bar)}.should_not raise_error + end + + it "should allow specifying numerous indirections which are to be no longer accessible to clients" do + @server.register(:foo, :bar) + lambda { @server.unregister(:foo, :bar) }.should_not raise_error + end + + it "should not turn off any indirections if given unknown indirection names to turn off" do + @server.register(:foo, :bar) + lambda { @server.unregister(:foo, :bar, :baz) }.should raise_error(ArgumentError) + lambda { @server.unregister(:foo, :bar) }.should_not raise_error + end + + it "should not allow turning off unknown indirection names" do + @server.register(:foo, :bar) + lambda { @server.unregister(:baz) }.should raise_error(ArgumentError) + end + + it "should disable client access immediately when turning off indirections" do + @server.register(:foo, :bar) + @server.unregister(:foo) + lambda { @server.unregister(:foo) }.should raise_error(ArgumentError) + end + + it "should allow turning off all indirections at once" do + @server.register(:foo, :bar) + @server.unregister + [ :foo, :bar, :baz].each do |indirection| + lambda { @server.unregister(indirection) }.should raise_error(ArgumentError) + end + end + end + + it "should provide a means of determining whether it is listening" do + @server.should respond_to(:listening?) + end + + it "should provide a means of determining which HTTP server will be used to provide access to clients" do + @server.server_type.should == :suparserver + end + + it "should provide a means of determining which protocols are in use" do + @server.should respond_to(:protocols) + end + + it "should set the protocols to :rest and :xmlrpc" do + @server.protocols.should == [ :rest, :xmlrpc ] + end + + it "should provide a means of determining the listening address" do + @server.address.should == "127.0.0.1" + end + + it "should provide a means of determining the listening port" do + @server.port.should == 31337 + end + + it "should allow for multiple configurations, each handling different indirections" do + Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') + + @server2 = Puppet::Network::Server.new(:port => 31337) + @server.register(:foo, :bar) + @server2.register(:foo, :xyzzy) + @server.unregister(:foo, :bar) + @server2.unregister(:foo, :xyzzy) + lambda { @server.unregister(:xyzzy) }.should raise_error(ArgumentError) + lambda { @server2.unregister(:bar) }.should raise_error(ArgumentError) + end + + describe "when managing xmlrpc registrations" do + before do + Puppet::Network::Handler.stubs(:handler).returns mock('xmlrpc_handler') + end + + it "should allow registering an xmlrpc handler by specifying its namespace" do + lambda { @server.register_xmlrpc(:foo) }.should_not raise_error + end + + it "should require that the xmlrpc namespace be valid" do + Puppet::Network::Handler.stubs(:handler).returns nil + + lambda { @server.register_xmlrpc(:foo) }.should raise_error(ArgumentError) + end + + it "should require at least one namespace" do + lambda { @server.register_xmlrpc() }.should raise_error(ArgumentError) + end + + it "should allow multiple namespaces to be registered at once" do + lambda { @server.register_xmlrpc(:foo, :bar) }.should_not raise_error + end + + it "should allow the use of namespaces to specify which are no longer accessible to clients" do + @server.register_xmlrpc(:foo, :bar) + end + + it "should leave other namespaces accessible to clients when turning off xmlrpc namespaces" do + @server.register_xmlrpc(:foo, :bar) + @server.unregister_xmlrpc(:foo) + lambda { @server.unregister_xmlrpc(:bar)}.should_not raise_error + end + + it "should allow specifying numerous namespaces which are to be no longer accessible to clients" do + @server.register_xmlrpc(:foo, :bar) + lambda { @server.unregister_xmlrpc(:foo, :bar) }.should_not raise_error + end + + it "should not turn off any indirections if given unknown namespaces to turn off" do + @server.register_xmlrpc(:foo, :bar) + lambda { @server.unregister_xmlrpc(:foo, :bar, :baz) }.should raise_error(ArgumentError) + lambda { @server.unregister_xmlrpc(:foo, :bar) }.should_not raise_error + end + + it "should not allow turning off unknown namespaces" do + @server.register_xmlrpc(:foo, :bar) + lambda { @server.unregister_xmlrpc(:baz) }.should raise_error(ArgumentError) + end + + it "should disable client access immediately when turning off namespaces" do + @server.register_xmlrpc(:foo, :bar) + @server.unregister_xmlrpc(:foo) + lambda { @server.unregister_xmlrpc(:foo) }.should raise_error(ArgumentError) + end + + it "should allow turning off all namespaces at once" do + @server.register_xmlrpc(:foo, :bar) + @server.unregister_xmlrpc + [ :foo, :bar, :baz].each do |indirection| + lambda { @server.unregister_xmlrpc(indirection) }.should raise_error(ArgumentError) + end + end + end + + describe "when listening is off" do + before do + @mock_http_server = mock('http server') + @mock_http_server.stubs(:listen) + @server.stubs(:http_server).returns(@mock_http_server) + end + + it "should indicate that it is not listening" do + @server.should_not be_listening + end + + it "should not allow listening to be turned off" do + lambda { @server.unlisten }.should raise_error(RuntimeError) + end + + it "should allow listening to be turned on" do + lambda { @server.listen }.should_not raise_error + end + + end + + describe "when listening is on" do + before do + @mock_http_server = mock('http server') + @mock_http_server.stubs(:listen) + @mock_http_server.stubs(:unlisten) + @server.stubs(:http_server).returns(@mock_http_server) + @server.listen + end + + it "should indicate that it is listening" do + @server.should be_listening + end + + it "should not allow listening to be turned on" do + lambda { @server.listen }.should raise_error(RuntimeError) + end + + it "should allow listening to be turned off" do + lambda { @server.unlisten }.should_not raise_error + end + end + + describe "when listening is being turned on" do + before do + Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') + Puppet::Network::Handler.stubs(:handler).returns mock('xmlrpc_handler') + + @server = Puppet::Network::Server.new(:port => 31337, :handlers => [:node], :xmlrpc_handlers => [:master]) + @mock_http_server = mock('http server') + @mock_http_server.stubs(:listen) + end + + it "should fetch an instance of an HTTP server" do + @server.stubs(:http_server_class).returns(@mock_http_server_class) + @mock_http_server_class.expects(:new).returns(@mock_http_server) + @server.listen + end + + it "should cause the HTTP server to listen" do + @server.stubs(:http_server).returns(@mock_http_server) + @mock_http_server.expects(:listen) + @server.listen + end + + it "should pass the listening address to the HTTP server" do + @server.stubs(:http_server).returns(@mock_http_server) + @mock_http_server.expects(:listen).with do |args| + args[:address] == '127.0.0.1' + end + @server.listen + end + + it "should pass the listening port to the HTTP server" do + @server.stubs(:http_server).returns(@mock_http_server) + @mock_http_server.expects(:listen).with do |args| + args[:port] == 31337 + end + @server.listen + end + + it "should pass a list of REST handlers to the HTTP server" do + @server.stubs(:http_server).returns(@mock_http_server) + @mock_http_server.expects(:listen).with do |args| + args[:handlers] == [ :node ] + end + @server.listen + end + + it "should pass a list of XMLRPC handlers to the HTTP server" do + @server.stubs(:http_server).returns(@mock_http_server) + @mock_http_server.expects(:listen).with do |args| + args[:xmlrpc_handlers] == [ :master ] + end + @server.listen + end + + it "should pass a list of protocols to the HTTP server" do + @server.stubs(:http_server).returns(@mock_http_server) + @mock_http_server.expects(:listen).with do |args| + args[:protocols] == [ :rest, :xmlrpc ] + end + @server.listen + end + end + + describe "when listening is being turned off" do + before do + @mock_http_server = mock('http server') + @mock_http_server.stubs(:listen) + @server.stubs(:http_server).returns(@mock_http_server) + @server.listen + end + + it "should cause the HTTP server to stop listening" do + @mock_http_server.expects(:unlisten) + @server.unlisten + end + + it "should not allow for indirections to be turned off" do + Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') + + @server.register(:foo) + lambda { @server.unregister(:foo) }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/unit/network/server_spec_spec.rb b/spec/unit/network/server_spec_spec.rb deleted file mode 100755 index 2aeb99a8b..000000000 --- a/spec/unit/network/server_spec_spec.rb +++ /dev/null @@ -1,534 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Rick Bradley on 2007-10-03. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/network/server' - -describe Puppet::Network::Server do - before do - @mock_http_server_class = mock('http server class') - Puppet.settings.stubs(:use) - Puppet.settings.stubs(:value).with(:name).returns("me") - Puppet.settings.stubs(:value).with(:servertype).returns(:suparserver) - Puppet.settings.stubs(:value).with(:bindaddress).returns("") - Puppet.settings.stubs(:value).with(:masterport).returns(8140) - Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) - Puppet.settings.stubs(:value).with(:servertype).returns(:suparserver) - @server = Puppet::Network::Server.new(:port => 31337) - end - - describe "when initializing" do - before do - Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') - Puppet::Network::Handler.stubs(:handler).returns mock('xmlrpc_handler') - Puppet.settings.stubs(:value).with(:bindaddress).returns("") - Puppet.settings.stubs(:value).with(:masterport).returns('') - end - - it 'should fail if an unknown option is provided' do - lambda { Puppet::Network::Server.new(:foo => 31337) }.should raise_error(ArgumentError) - end - - it "should allow specifying a listening port" do - Puppet.settings.stubs(:value).with(:bindaddress).returns('') - @server = Puppet::Network::Server.new(:port => 31337) - @server.port.should == 31337 - end - - it "should use the :bindaddress setting to determine the default listening address" do - Puppet.settings.stubs(:value).with(:masterport).returns('') - Puppet.settings.expects(:value).with(:bindaddress).returns("10.0.0.1") - @server = Puppet::Network::Server.new - @server.address.should == "10.0.0.1" - end - - it "should set the bind address to '127.0.0.1' if the default address is an empty string and the server type is mongrel" do - Puppet.settings.stubs(:value).with(:servertype).returns("mongrel") - Puppet.settings.expects(:value).with(:bindaddress).returns("") - @server = Puppet::Network::Server.new - @server.address.should == '127.0.0.1' - end - - it "should set the bind address to '0.0.0.0' if the default address is an empty string and the server type is webrick" do - Puppet.settings.stubs(:value).with(:servertype).returns("webrick") - Puppet.settings.expects(:value).with(:bindaddress).returns("") - @server = Puppet::Network::Server.new - @server.address.should == '0.0.0.0' - end - - it "should use the Puppet configurator to find a default listening port" do - Puppet.settings.stubs(:value).with(:bindaddress).returns('') - Puppet.settings.expects(:value).with(:masterport).returns(6667) - @server = Puppet::Network::Server.new - @server.port.should == 6667 - end - - it "should fail to initialize if no listening port can be found" do - Puppet.settings.stubs(:value).with(:bindaddress).returns("127.0.0.1") - Puppet.settings.stubs(:value).with(:masterport).returns(nil) - lambda { Puppet::Network::Server.new }.should raise_error(ArgumentError) - end - - it "should use the Puppet configurator to determine which HTTP server will be used to provide access to clients" do - Puppet.settings.expects(:value).with(:servertype).returns(:suparserver) - @server = Puppet::Network::Server.new(:port => 31337) - @server.server_type.should == :suparserver - end - - it "should fail to initialize if there is no HTTP server known to the Puppet configurator" do - Puppet.settings.expects(:value).with(:servertype).returns(nil) - lambda { Puppet::Network::Server.new(:port => 31337) }.should raise_error - end - - it "should ask the Puppet::Network::HTTP class to fetch the proper HTTP server class" do - Puppet::Network::HTTP.expects(:server_class_by_type).with(:suparserver).returns(@mock_http_server_class) - @server = Puppet::Network::Server.new(:port => 31337) - end - - it "should fail if the HTTP server class is unknown" do - Puppet::Network::HTTP.stubs(:server_class_by_type).returns(nil) - lambda { Puppet::Network::Server.new(:port => 31337) }.should raise_error(ArgumentError) - end - - it "should allow registering REST handlers" do - @server = Puppet::Network::Server.new(:port => 31337, :handlers => [ :foo, :bar, :baz]) - lambda { @server.unregister(:foo, :bar, :baz) }.should_not raise_error - end - - it "should allow registering XMLRPC handlers" do - @server = Puppet::Network::Server.new(:port => 31337, :xmlrpc_handlers => [ :foo, :bar, :baz]) - lambda { @server.unregister_xmlrpc(:foo, :bar, :baz) }.should_not raise_error - end - - it "should not be listening after initialization" do - Puppet::Network::Server.new(:port => 31337).should_not be_listening - end - - it "should use the :main setting section" do - Puppet.settings.expects(:use).with { |*args| args.include?(:main) } - @server = Puppet::Network::Server.new(:port => 31337, :xmlrpc_handlers => [ :foo, :bar, :baz]) - end - - it "should use the Puppet[:name] setting section" do - Puppet.settings.expects(:value).with(:name).returns "me" - Puppet.settings.expects(:use).with { |*args| args.include?("me") } - - @server = Puppet::Network::Server.new(:port => 31337, :xmlrpc_handlers => [ :foo, :bar, :baz]) - end - end - - # We don't test the method, because it's too much of a Unix-y pain. - it "should be able to daemonize" do - @server.should respond_to(:daemonize) - end - - describe "when being started" do - before do - @server.stubs(:listen) - @server.stubs(:create_pidfile) - end - - it "should listen" do - @server.expects(:listen) - @server.start - end - - it "should create its PID file" do - @server.expects(:create_pidfile) - @server.start - end - end - - describe "when being stopped" do - before do - @server.stubs(:unlisten) - @server.stubs(:remove_pidfile) - end - - it "should unlisten" do - @server.expects(:unlisten) - @server.stop - end - - it "should remove its PID file" do - @server.expects(:remove_pidfile) - @server.stop - end - end - - describe "when creating its pidfile" do - it "should use an exclusive mutex" do - Puppet.settings.expects(:value).with(:name).returns "me" - - sync = mock 'sync' - Puppet::Util.expects(:sync).with("me").returns sync - - sync.expects(:synchronize).with(Sync::EX) - @server.create_pidfile - end - - it "should lock the pidfile using the Pidlock class" do - pidfile = mock 'pidfile' - - Puppet.settings.stubs(:value).with(:name).returns "eh" - Puppet.settings.expects(:value).with(:pidfile).returns "/my/file" - - Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile - - pidfile.expects(:lock).returns true - @server.create_pidfile - end - - it "should fail if it cannot lock" do - pidfile = mock 'pidfile' - - Puppet.settings.stubs(:value).with(:name).returns "eh" - Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" - - Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile - - pidfile.expects(:lock).returns false - - lambda { @server.create_pidfile }.should raise_error - end - end - - describe "when removing its pidfile" do - it "should use an exclusive mutex" do - Puppet.settings.expects(:value).with(:name).returns "me" - - sync = mock 'sync' - Puppet::Util.expects(:sync).with("me").returns sync - - sync.expects(:synchronize).with(Sync::EX) - @server.remove_pidfile - end - - it "should do nothing if the pidfile is not present" do - pidfile = mock 'pidfile', :locked? => false - Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile - - Puppet.settings.stubs(:value).with(:name).returns "eh" - Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" - - pidfile.expects(:unlock).never - @server.remove_pidfile - end - - it "should unlock the pidfile using the Pidlock class" do - pidfile = mock 'pidfile', :locked? => true - Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile - pidfile.expects(:unlock).returns true - - Puppet.settings.stubs(:value).with(:name).returns "eh" - Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" - - @server.remove_pidfile - end - - it "should warn if it cannot remove the pidfile" do - pidfile = mock 'pidfile', :locked? => true - Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile - pidfile.expects(:unlock).returns false - - Puppet.settings.stubs(:value).with(:name).returns "eh" - Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" - - Puppet.expects :err - @server.remove_pidfile - end - end - - describe "when managing indirection registrations" do - before do - Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') - end - - it "should allow registering an indirection for client access by specifying its indirection name" do - lambda { @server.register(:foo) }.should_not raise_error - end - - it "should require that the indirection be valid" do - Puppet::Indirector::Indirection.expects(:model).with(:foo).returns nil - lambda { @server.register(:foo) }.should raise_error(ArgumentError) - end - - it "should require at least one indirection name when registering indirections for client access" do - lambda { @server.register }.should raise_error(ArgumentError) - end - - it "should allow for numerous indirections to be registered at once for client access" do - lambda { @server.register(:foo, :bar, :baz) }.should_not raise_error - end - - it "should allow the use of indirection names to specify which indirections are to be no longer accessible to clients" do - @server.register(:foo) - lambda { @server.unregister(:foo) }.should_not raise_error - end - - it "should leave other indirections accessible to clients when turning off indirections" do - @server.register(:foo, :bar) - @server.unregister(:foo) - lambda { @server.unregister(:bar)}.should_not raise_error - end - - it "should allow specifying numerous indirections which are to be no longer accessible to clients" do - @server.register(:foo, :bar) - lambda { @server.unregister(:foo, :bar) }.should_not raise_error - end - - it "should not turn off any indirections if given unknown indirection names to turn off" do - @server.register(:foo, :bar) - lambda { @server.unregister(:foo, :bar, :baz) }.should raise_error(ArgumentError) - lambda { @server.unregister(:foo, :bar) }.should_not raise_error - end - - it "should not allow turning off unknown indirection names" do - @server.register(:foo, :bar) - lambda { @server.unregister(:baz) }.should raise_error(ArgumentError) - end - - it "should disable client access immediately when turning off indirections" do - @server.register(:foo, :bar) - @server.unregister(:foo) - lambda { @server.unregister(:foo) }.should raise_error(ArgumentError) - end - - it "should allow turning off all indirections at once" do - @server.register(:foo, :bar) - @server.unregister - [ :foo, :bar, :baz].each do |indirection| - lambda { @server.unregister(indirection) }.should raise_error(ArgumentError) - end - end - end - - it "should provide a means of determining whether it is listening" do - @server.should respond_to(:listening?) - end - - it "should provide a means of determining which HTTP server will be used to provide access to clients" do - @server.server_type.should == :suparserver - end - - it "should provide a means of determining which protocols are in use" do - @server.should respond_to(:protocols) - end - - it "should set the protocols to :rest and :xmlrpc" do - @server.protocols.should == [ :rest, :xmlrpc ] - end - - it "should provide a means of determining the listening address" do - @server.address.should == "127.0.0.1" - end - - it "should provide a means of determining the listening port" do - @server.port.should == 31337 - end - - it "should allow for multiple configurations, each handling different indirections" do - Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') - - @server2 = Puppet::Network::Server.new(:port => 31337) - @server.register(:foo, :bar) - @server2.register(:foo, :xyzzy) - @server.unregister(:foo, :bar) - @server2.unregister(:foo, :xyzzy) - lambda { @server.unregister(:xyzzy) }.should raise_error(ArgumentError) - lambda { @server2.unregister(:bar) }.should raise_error(ArgumentError) - end - - describe "when managing xmlrpc registrations" do - before do - Puppet::Network::Handler.stubs(:handler).returns mock('xmlrpc_handler') - end - - it "should allow registering an xmlrpc handler by specifying its namespace" do - lambda { @server.register_xmlrpc(:foo) }.should_not raise_error - end - - it "should require that the xmlrpc namespace be valid" do - Puppet::Network::Handler.stubs(:handler).returns nil - - lambda { @server.register_xmlrpc(:foo) }.should raise_error(ArgumentError) - end - - it "should require at least one namespace" do - lambda { @server.register_xmlrpc() }.should raise_error(ArgumentError) - end - - it "should allow multiple namespaces to be registered at once" do - lambda { @server.register_xmlrpc(:foo, :bar) }.should_not raise_error - end - - it "should allow the use of namespaces to specify which are no longer accessible to clients" do - @server.register_xmlrpc(:foo, :bar) - end - - it "should leave other namespaces accessible to clients when turning off xmlrpc namespaces" do - @server.register_xmlrpc(:foo, :bar) - @server.unregister_xmlrpc(:foo) - lambda { @server.unregister_xmlrpc(:bar)}.should_not raise_error - end - - it "should allow specifying numerous namespaces which are to be no longer accessible to clients" do - @server.register_xmlrpc(:foo, :bar) - lambda { @server.unregister_xmlrpc(:foo, :bar) }.should_not raise_error - end - - it "should not turn off any indirections if given unknown namespaces to turn off" do - @server.register_xmlrpc(:foo, :bar) - lambda { @server.unregister_xmlrpc(:foo, :bar, :baz) }.should raise_error(ArgumentError) - lambda { @server.unregister_xmlrpc(:foo, :bar) }.should_not raise_error - end - - it "should not allow turning off unknown namespaces" do - @server.register_xmlrpc(:foo, :bar) - lambda { @server.unregister_xmlrpc(:baz) }.should raise_error(ArgumentError) - end - - it "should disable client access immediately when turning off namespaces" do - @server.register_xmlrpc(:foo, :bar) - @server.unregister_xmlrpc(:foo) - lambda { @server.unregister_xmlrpc(:foo) }.should raise_error(ArgumentError) - end - - it "should allow turning off all namespaces at once" do - @server.register_xmlrpc(:foo, :bar) - @server.unregister_xmlrpc - [ :foo, :bar, :baz].each do |indirection| - lambda { @server.unregister_xmlrpc(indirection) }.should raise_error(ArgumentError) - end - end - end - - describe "when listening is off" do - before do - @mock_http_server = mock('http server') - @mock_http_server.stubs(:listen) - @server.stubs(:http_server).returns(@mock_http_server) - end - - it "should indicate that it is not listening" do - @server.should_not be_listening - end - - it "should not allow listening to be turned off" do - lambda { @server.unlisten }.should raise_error(RuntimeError) - end - - it "should allow listening to be turned on" do - lambda { @server.listen }.should_not raise_error - end - - end - - describe "when listening is on" do - before do - @mock_http_server = mock('http server') - @mock_http_server.stubs(:listen) - @mock_http_server.stubs(:unlisten) - @server.stubs(:http_server).returns(@mock_http_server) - @server.listen - end - - it "should indicate that it is listening" do - @server.should be_listening - end - - it "should not allow listening to be turned on" do - lambda { @server.listen }.should raise_error(RuntimeError) - end - - it "should allow listening to be turned off" do - lambda { @server.unlisten }.should_not raise_error - end - end - - describe "when listening is being turned on" do - before do - Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') - Puppet::Network::Handler.stubs(:handler).returns mock('xmlrpc_handler') - - @server = Puppet::Network::Server.new(:port => 31337, :handlers => [:node], :xmlrpc_handlers => [:master]) - @mock_http_server = mock('http server') - @mock_http_server.stubs(:listen) - end - - it "should fetch an instance of an HTTP server" do - @server.stubs(:http_server_class).returns(@mock_http_server_class) - @mock_http_server_class.expects(:new).returns(@mock_http_server) - @server.listen - end - - it "should cause the HTTP server to listen" do - @server.stubs(:http_server).returns(@mock_http_server) - @mock_http_server.expects(:listen) - @server.listen - end - - it "should pass the listening address to the HTTP server" do - @server.stubs(:http_server).returns(@mock_http_server) - @mock_http_server.expects(:listen).with do |args| - args[:address] == '127.0.0.1' - end - @server.listen - end - - it "should pass the listening port to the HTTP server" do - @server.stubs(:http_server).returns(@mock_http_server) - @mock_http_server.expects(:listen).with do |args| - args[:port] == 31337 - end - @server.listen - end - - it "should pass a list of REST handlers to the HTTP server" do - @server.stubs(:http_server).returns(@mock_http_server) - @mock_http_server.expects(:listen).with do |args| - args[:handlers] == [ :node ] - end - @server.listen - end - - it "should pass a list of XMLRPC handlers to the HTTP server" do - @server.stubs(:http_server).returns(@mock_http_server) - @mock_http_server.expects(:listen).with do |args| - args[:xmlrpc_handlers] == [ :master ] - end - @server.listen - end - - it "should pass a list of protocols to the HTTP server" do - @server.stubs(:http_server).returns(@mock_http_server) - @mock_http_server.expects(:listen).with do |args| - args[:protocols] == [ :rest, :xmlrpc ] - end - @server.listen - end - end - - describe "when listening is being turned off" do - before do - @mock_http_server = mock('http server') - @mock_http_server.stubs(:listen) - @server.stubs(:http_server).returns(@mock_http_server) - @server.listen - end - - it "should cause the HTTP server to stop listening" do - @mock_http_server.expects(:unlisten) - @server.unlisten - end - - it "should not allow for indirections to be turned off" do - Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') - - @server.register(:foo) - lambda { @server.unregister(:foo) }.should raise_error(RuntimeError) - end - end -end diff --git a/spec/unit/network/xmlrpc/client_spec.rb b/spec/unit/network/xmlrpc/client_spec.rb new file mode 100755 index 000000000..1b97583a7 --- /dev/null +++ b/spec/unit/network/xmlrpc/client_spec.rb @@ -0,0 +1,171 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +describe Puppet::Network::XMLRPCClient do + describe "when performing the rpc call" do + before do + Puppet::SSL::Host.any_instance.stubs(:certificate_matches_key?).returns true + @client = Puppet::Network::Client.report.xmlrpc_client.new + @client.stubs(:call).returns "foo" + end + + it "should call the specified namespace and method, with the specified arguments" do + @client.expects(:call).with("puppetreports.report", "eh").returns "foo" + @client.report("eh") + end + + it "should return the results from the call" do + @client.expects(:call).returns "foo" + @client.report("eh").should == "foo" + end + + it "should always close the http connection if it is still open after the call" do + http = mock 'http' + @client.stubs(:http).returns http + + http.expects(:started?).returns true + http.expects(:finish) + + @client.report("eh").should == "foo" + end + + it "should always close the http connection if it is still open after a call that raises an exception" do + http = mock 'http' + @client.stubs(:http).returns http + + @client.expects(:call).raises RuntimeError + + http.expects(:started?).returns true + http.expects(:finish) + + lambda { @client.report("eh") }.should raise_error + end + + describe "when returning the http instance" do + it "should use the http pool to create the instance" do + @client.instance_variable_set("@http", nil) + @client.expects(:host).returns "myhost" + @client.expects(:port).returns "myport" + Puppet::Network::HttpPool.expects(:http_instance).with("myhost", "myport", true).returns "http" + + @client.http.should == "http" + end + + it "should reuse existing instances" do + @client.http.should equal(@client.http) + end + end + + describe "when recycling the connection" do + it "should close the existing instance if it's open" do + http = mock 'http' + @client.stubs(:http).returns http + + http.expects(:started?).returns true + http.expects(:finish) + + @client.recycle_connection + end + + it "should force creation of a new instance" do + Puppet::Network::HttpPool.expects(:http_instance).returns "second_http" + + @client.recycle_connection + + @client.http.should == "second_http" + end + end + + describe "and an exception is raised" do + it "should raise XMLRPCClientError if XMLRPC::FaultException is raised" do + error = XMLRPC::FaultException.new("foo", "bar") + + @client.expects(:call).raises(error) + + lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) + end + + it "should raise XMLRPCClientError if Errno::ECONNREFUSED is raised" do + @client.expects(:call).raises(Errno::ECONNREFUSED) + + lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) + end + + it "should log and raise XMLRPCClientError if Timeout::Error is raised" do + Puppet.expects(:err) + @client.expects(:call).raises(Timeout::Error) + + lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) + end + + it "should log and raise XMLRPCClientError if SocketError is raised" do + Puppet.expects(:err) + @client.expects(:call).raises(SocketError) + + lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) + end + + it "should log, recycle the connection, and retry if Errno::EPIPE is raised" do + @client.expects(:call).times(2).raises(Errno::EPIPE).then.returns "eh" + + Puppet.expects(:info) + @client.expects(:recycle_connection) + + @client.report("eh") + end + + it "should log, recycle the connection, and retry if EOFError is raised" do + @client.expects(:call).times(2).raises(EOFError).then.returns "eh" + + Puppet.expects(:info) + @client.expects(:recycle_connection) + + @client.report("eh") + end + + it "should log and retry if an exception containing 'Wrong size' is raised" do + error = RuntimeError.new("Wrong size. Was 15, should be 30") + @client.expects(:call).times(2).raises(error).then.returns "eh" + + Puppet.expects(:warning) + + @client.report("eh") + end + + it "should raise XMLRPCClientError if OpenSSL::SSL::SSLError is raised" do + @client.expects(:call).raises(OpenSSL::SSL::SSLError) + + lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) + end + + it "should log and raise XMLRPCClientError if OpenSSL::SSL::SSLError is raised with certificate issues" do + error = OpenSSL::SSL::SSLError.new("hostname was not match") + @client.expects(:call).raises(error) + + Puppet.expects(:warning) + + lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) + end + + it "should log, recycle the connection, and retry if OpenSSL::SSL::SSLError is raised containing 'bad write retry'" do + error = OpenSSL::SSL::SSLError.new("bad write retry") + @client.expects(:call).times(2).raises(error).then.returns "eh" + + @client.expects(:recycle_connection) + + Puppet.expects(:warning) + + @client.report("eh") + end + + it "should log and raise XMLRPCClientError if any other exception is raised" do + @client.expects(:call).raises(RuntimeError) + + Puppet.expects(:err) + + lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) + end + end + end +end diff --git a/spec/unit/network/xmlrpc/client_spec_spec.rb b/spec/unit/network/xmlrpc/client_spec_spec.rb deleted file mode 100755 index 1b97583a7..000000000 --- a/spec/unit/network/xmlrpc/client_spec_spec.rb +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -describe Puppet::Network::XMLRPCClient do - describe "when performing the rpc call" do - before do - Puppet::SSL::Host.any_instance.stubs(:certificate_matches_key?).returns true - @client = Puppet::Network::Client.report.xmlrpc_client.new - @client.stubs(:call).returns "foo" - end - - it "should call the specified namespace and method, with the specified arguments" do - @client.expects(:call).with("puppetreports.report", "eh").returns "foo" - @client.report("eh") - end - - it "should return the results from the call" do - @client.expects(:call).returns "foo" - @client.report("eh").should == "foo" - end - - it "should always close the http connection if it is still open after the call" do - http = mock 'http' - @client.stubs(:http).returns http - - http.expects(:started?).returns true - http.expects(:finish) - - @client.report("eh").should == "foo" - end - - it "should always close the http connection if it is still open after a call that raises an exception" do - http = mock 'http' - @client.stubs(:http).returns http - - @client.expects(:call).raises RuntimeError - - http.expects(:started?).returns true - http.expects(:finish) - - lambda { @client.report("eh") }.should raise_error - end - - describe "when returning the http instance" do - it "should use the http pool to create the instance" do - @client.instance_variable_set("@http", nil) - @client.expects(:host).returns "myhost" - @client.expects(:port).returns "myport" - Puppet::Network::HttpPool.expects(:http_instance).with("myhost", "myport", true).returns "http" - - @client.http.should == "http" - end - - it "should reuse existing instances" do - @client.http.should equal(@client.http) - end - end - - describe "when recycling the connection" do - it "should close the existing instance if it's open" do - http = mock 'http' - @client.stubs(:http).returns http - - http.expects(:started?).returns true - http.expects(:finish) - - @client.recycle_connection - end - - it "should force creation of a new instance" do - Puppet::Network::HttpPool.expects(:http_instance).returns "second_http" - - @client.recycle_connection - - @client.http.should == "second_http" - end - end - - describe "and an exception is raised" do - it "should raise XMLRPCClientError if XMLRPC::FaultException is raised" do - error = XMLRPC::FaultException.new("foo", "bar") - - @client.expects(:call).raises(error) - - lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) - end - - it "should raise XMLRPCClientError if Errno::ECONNREFUSED is raised" do - @client.expects(:call).raises(Errno::ECONNREFUSED) - - lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) - end - - it "should log and raise XMLRPCClientError if Timeout::Error is raised" do - Puppet.expects(:err) - @client.expects(:call).raises(Timeout::Error) - - lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) - end - - it "should log and raise XMLRPCClientError if SocketError is raised" do - Puppet.expects(:err) - @client.expects(:call).raises(SocketError) - - lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) - end - - it "should log, recycle the connection, and retry if Errno::EPIPE is raised" do - @client.expects(:call).times(2).raises(Errno::EPIPE).then.returns "eh" - - Puppet.expects(:info) - @client.expects(:recycle_connection) - - @client.report("eh") - end - - it "should log, recycle the connection, and retry if EOFError is raised" do - @client.expects(:call).times(2).raises(EOFError).then.returns "eh" - - Puppet.expects(:info) - @client.expects(:recycle_connection) - - @client.report("eh") - end - - it "should log and retry if an exception containing 'Wrong size' is raised" do - error = RuntimeError.new("Wrong size. Was 15, should be 30") - @client.expects(:call).times(2).raises(error).then.returns "eh" - - Puppet.expects(:warning) - - @client.report("eh") - end - - it "should raise XMLRPCClientError if OpenSSL::SSL::SSLError is raised" do - @client.expects(:call).raises(OpenSSL::SSL::SSLError) - - lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) - end - - it "should log and raise XMLRPCClientError if OpenSSL::SSL::SSLError is raised with certificate issues" do - error = OpenSSL::SSL::SSLError.new("hostname was not match") - @client.expects(:call).raises(error) - - Puppet.expects(:warning) - - lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) - end - - it "should log, recycle the connection, and retry if OpenSSL::SSL::SSLError is raised containing 'bad write retry'" do - error = OpenSSL::SSL::SSLError.new("bad write retry") - @client.expects(:call).times(2).raises(error).then.returns "eh" - - @client.expects(:recycle_connection) - - Puppet.expects(:warning) - - @client.report("eh") - end - - it "should log and raise XMLRPCClientError if any other exception is raised" do - @client.expects(:call).raises(RuntimeError) - - Puppet.expects(:err) - - lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) - end - end - end -end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb new file mode 100755 index 000000000..d0db2504c --- /dev/null +++ b/spec/unit/node/environment_spec.rb @@ -0,0 +1,248 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/node/environment' +require 'puppet/util/execution' + +describe Puppet::Node::Environment do + after do + Puppet::Node::Environment.clear + end + + it "should include the Cacher module" do + Puppet::Node::Environment.ancestors.should be_include(Puppet::Util::Cacher) + end + + it "should use the filetimeout for the ttl for the modulepath" do + Puppet::Node::Environment.attr_ttl(:modulepath).should == Integer(Puppet[:filetimeout]) + end + + it "should use the filetimeout for the ttl for the module list" do + Puppet::Node::Environment.attr_ttl(:modules).should == Integer(Puppet[:filetimeout]) + end + + it "should use the filetimeout for the ttl for the manifestdir" do + Puppet::Node::Environment.attr_ttl(:manifestdir).should == Integer(Puppet[:filetimeout]) + end + + it "should use the default environment if no name is provided while initializing an environment" do + Puppet.settings.expects(:value).with(:environment).returns("one") + Puppet::Node::Environment.new().name.should == :one + end + + it "should treat environment instances as singletons" do + Puppet::Node::Environment.new("one").should equal(Puppet::Node::Environment.new("one")) + end + + it "should treat an environment specified as names or strings as equivalent" do + Puppet::Node::Environment.new(:one).should equal(Puppet::Node::Environment.new("one")) + end + + it "should return its name when converted to a string" do + Puppet::Node::Environment.new(:one).to_s.should == "one" + end + + it "should just return any provided environment if an environment is provided as the name" do + one = Puppet::Node::Environment.new(:one) + Puppet::Node::Environment.new(one).should equal(one) + end + + describe "when managing known resource types" do + before do + @env = Puppet::Node::Environment.new("dev") + @collection = Puppet::Resource::TypeCollection.new(@env) + @collection.stubs(:perform_initial_import) + end + + it "should create a resource type collection if none exists" do + Puppet::Resource::TypeCollection.expects(:new).with(@env).returns @collection + @env.known_resource_types.should equal(@collection) + end + + it "should reuse any existing resource type collection" do + @env.known_resource_types.should equal(@env.known_resource_types) + end + + it "should perform the initial import when creating a new collection" do + @collection.expects(:perform_initial_import) + Puppet::Resource::TypeCollection.expects(:new).returns @collection + + @env.known_resource_types + end + + it "should create and return a new collection rather than returning a stale collection" do + @env.known_resource_types.expects(:stale?).returns true + + Puppet::Resource::TypeCollection.expects(:new).returns @collection + + @env.known_resource_types.should equal(@collection) + end + end + + [:modulepath, :manifestdir].each do |setting| + it "should validate the #{setting} directories" do + path = %w{/one /two}.join(File::PATH_SEPARATOR) + + env = Puppet::Node::Environment.new("testing") + env.stubs(:[]).with(setting).returns path + + env.expects(:validate_dirs).with(%w{/one /two}) + + env.send(setting) + end + + it "should return the validated dirs for #{setting}" do + path = %w{/one /two}.join(File::PATH_SEPARATOR) + + env = Puppet::Node::Environment.new("testing") + env.stubs(:[]).with(setting).returns path + env.stubs(:validate_dirs).returns %w{/one /two} + + env.send(setting).should == %w{/one /two} + end + end + + it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do + Puppet::Util::Execution.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do + env = Puppet::Node::Environment.new("testing") + module_path = %w{/one /two}.join(File::PATH_SEPARATOR) + env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two} + env.expects(:[]).with(:modulepath).returns module_path + + env.modulepath.should == %w{/l1 /l2 /one /two} + end + end + + describe "when validating modulepath or manifestdir directories" do + it "should not return non-directories" do + env = Puppet::Node::Environment.new("testing") + + FileTest.expects(:directory?).with("/one").returns true + FileTest.expects(:directory?).with("/two").returns false + + env.validate_dirs(%w{/one /two}).should == %w{/one} + end + + it "should use the current working directory to fully-qualify unqualified paths" do + FileTest.stubs(:directory?).returns true + env = Puppet::Node::Environment.new("testing") + + two = File.join(Dir.getwd, "two") + env.validate_dirs(%w{/one two}).should == ["/one", two] + end + end + + describe "when modeling a specific environment" do + it "should have a method for returning the environment name" do + Puppet::Node::Environment.new("testing").name.should == :testing + end + + it "should provide an array-like accessor method for returning any environment-specific setting" do + env = Puppet::Node::Environment.new("testing") + env.should respond_to(:[]) + end + + it "should ask the Puppet settings instance for the setting qualified with the environment name" do + Puppet.settings.expects(:value).with("myvar", :testing).returns("myval") + env = Puppet::Node::Environment.new("testing") + env["myvar"].should == "myval" + end + + it "should be able to return an individual module that exists in its module path" do + env = Puppet::Node::Environment.new("testing") + + mod = mock 'module' + Puppet::Module.expects(:new).with("one", env).returns mod + mod.expects(:exist?).returns true + + env.module("one").should equal(mod) + end + + it "should return nil if asked for a module that does not exist in its path" do + env = Puppet::Node::Environment.new("testing") + + mod = mock 'module' + Puppet::Module.expects(:new).with("one", env).returns mod + mod.expects(:exist?).returns false + + env.module("one").should be_nil + end + + it "should be able to return its modules" do + Puppet::Node::Environment.new("testing").should respond_to(:modules) + end + + describe ".modules" do + it "should return a module named for every directory in each module path" do + env = Puppet::Node::Environment.new("testing") + env.expects(:modulepath).at_least_once.returns %w{/a /b} + Dir.expects(:entries).with("/a").returns %w{foo bar} + Dir.expects(:entries).with("/b").returns %w{bee baz} + + env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort + end + + it "should remove duplicates" do + env = Puppet::Node::Environment.new("testing") + env.expects(:modulepath).returns( %w{/a /b} ).at_least_once + Dir.expects(:entries).with("/a").returns %w{foo} + Dir.expects(:entries).with("/b").returns %w{foo} + + env.modules.collect{|mod| mod.name}.sort.should == %w{foo} + end + + it "should ignore invalid modules" do + env = Puppet::Node::Environment.new("testing") + env.stubs(:modulepath).returns %w{/a} + Dir.expects(:entries).with("/a").returns %w{foo bar} + + Puppet::Module.expects(:new).with { |name, env| name == "foo" }.returns mock("foomod", :name => "foo") + Puppet::Module.expects(:new).with { |name, env| name == "bar" }.raises( Puppet::Module::InvalidName, "name is invalid" ) + + env.modules.collect{|mod| mod.name}.sort.should == %w{foo} + end + + it "should create modules with the correct environment" do + env = Puppet::Node::Environment.new("testing") + env.expects(:modulepath).at_least_once.returns %w{/a} + Dir.expects(:entries).with("/a").returns %w{foo} + + env.modules.each {|mod| mod.environment.should == env } + end + + it "should cache the module list" do + env = Puppet::Node::Environment.new("testing") + env.expects(:modulepath).at_least_once.returns %w{/a} + Dir.expects(:entries).once.with("/a").returns %w{foo} + + env.modules + env.modules + end + end + end + + describe Puppet::Node::Environment::Helper do + before do + @helper = Object.new + @helper.extend(Puppet::Node::Environment::Helper) + end + + it "should be able to set and retrieve the environment" do + @helper.environment = :foo + @helper.environment.name.should == :foo + end + + it "should accept an environment directly" do + env = Puppet::Node::Environment.new :foo + @helper.environment = env + @helper.environment.name.should == :foo + end + + it "should accept an environment as a string" do + env = Puppet::Node::Environment.new "foo" + @helper.environment = env + @helper.environment.name.should == :foo + end + end +end diff --git a/spec/unit/node/environment_spec_spec.rb b/spec/unit/node/environment_spec_spec.rb deleted file mode 100755 index d0db2504c..000000000 --- a/spec/unit/node/environment_spec_spec.rb +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/node/environment' -require 'puppet/util/execution' - -describe Puppet::Node::Environment do - after do - Puppet::Node::Environment.clear - end - - it "should include the Cacher module" do - Puppet::Node::Environment.ancestors.should be_include(Puppet::Util::Cacher) - end - - it "should use the filetimeout for the ttl for the modulepath" do - Puppet::Node::Environment.attr_ttl(:modulepath).should == Integer(Puppet[:filetimeout]) - end - - it "should use the filetimeout for the ttl for the module list" do - Puppet::Node::Environment.attr_ttl(:modules).should == Integer(Puppet[:filetimeout]) - end - - it "should use the filetimeout for the ttl for the manifestdir" do - Puppet::Node::Environment.attr_ttl(:manifestdir).should == Integer(Puppet[:filetimeout]) - end - - it "should use the default environment if no name is provided while initializing an environment" do - Puppet.settings.expects(:value).with(:environment).returns("one") - Puppet::Node::Environment.new().name.should == :one - end - - it "should treat environment instances as singletons" do - Puppet::Node::Environment.new("one").should equal(Puppet::Node::Environment.new("one")) - end - - it "should treat an environment specified as names or strings as equivalent" do - Puppet::Node::Environment.new(:one).should equal(Puppet::Node::Environment.new("one")) - end - - it "should return its name when converted to a string" do - Puppet::Node::Environment.new(:one).to_s.should == "one" - end - - it "should just return any provided environment if an environment is provided as the name" do - one = Puppet::Node::Environment.new(:one) - Puppet::Node::Environment.new(one).should equal(one) - end - - describe "when managing known resource types" do - before do - @env = Puppet::Node::Environment.new("dev") - @collection = Puppet::Resource::TypeCollection.new(@env) - @collection.stubs(:perform_initial_import) - end - - it "should create a resource type collection if none exists" do - Puppet::Resource::TypeCollection.expects(:new).with(@env).returns @collection - @env.known_resource_types.should equal(@collection) - end - - it "should reuse any existing resource type collection" do - @env.known_resource_types.should equal(@env.known_resource_types) - end - - it "should perform the initial import when creating a new collection" do - @collection.expects(:perform_initial_import) - Puppet::Resource::TypeCollection.expects(:new).returns @collection - - @env.known_resource_types - end - - it "should create and return a new collection rather than returning a stale collection" do - @env.known_resource_types.expects(:stale?).returns true - - Puppet::Resource::TypeCollection.expects(:new).returns @collection - - @env.known_resource_types.should equal(@collection) - end - end - - [:modulepath, :manifestdir].each do |setting| - it "should validate the #{setting} directories" do - path = %w{/one /two}.join(File::PATH_SEPARATOR) - - env = Puppet::Node::Environment.new("testing") - env.stubs(:[]).with(setting).returns path - - env.expects(:validate_dirs).with(%w{/one /two}) - - env.send(setting) - end - - it "should return the validated dirs for #{setting}" do - path = %w{/one /two}.join(File::PATH_SEPARATOR) - - env = Puppet::Node::Environment.new("testing") - env.stubs(:[]).with(setting).returns path - env.stubs(:validate_dirs).returns %w{/one /two} - - env.send(setting).should == %w{/one /two} - end - end - - it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do - Puppet::Util::Execution.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do - env = Puppet::Node::Environment.new("testing") - module_path = %w{/one /two}.join(File::PATH_SEPARATOR) - env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two} - env.expects(:[]).with(:modulepath).returns module_path - - env.modulepath.should == %w{/l1 /l2 /one /two} - end - end - - describe "when validating modulepath or manifestdir directories" do - it "should not return non-directories" do - env = Puppet::Node::Environment.new("testing") - - FileTest.expects(:directory?).with("/one").returns true - FileTest.expects(:directory?).with("/two").returns false - - env.validate_dirs(%w{/one /two}).should == %w{/one} - end - - it "should use the current working directory to fully-qualify unqualified paths" do - FileTest.stubs(:directory?).returns true - env = Puppet::Node::Environment.new("testing") - - two = File.join(Dir.getwd, "two") - env.validate_dirs(%w{/one two}).should == ["/one", two] - end - end - - describe "when modeling a specific environment" do - it "should have a method for returning the environment name" do - Puppet::Node::Environment.new("testing").name.should == :testing - end - - it "should provide an array-like accessor method for returning any environment-specific setting" do - env = Puppet::Node::Environment.new("testing") - env.should respond_to(:[]) - end - - it "should ask the Puppet settings instance for the setting qualified with the environment name" do - Puppet.settings.expects(:value).with("myvar", :testing).returns("myval") - env = Puppet::Node::Environment.new("testing") - env["myvar"].should == "myval" - end - - it "should be able to return an individual module that exists in its module path" do - env = Puppet::Node::Environment.new("testing") - - mod = mock 'module' - Puppet::Module.expects(:new).with("one", env).returns mod - mod.expects(:exist?).returns true - - env.module("one").should equal(mod) - end - - it "should return nil if asked for a module that does not exist in its path" do - env = Puppet::Node::Environment.new("testing") - - mod = mock 'module' - Puppet::Module.expects(:new).with("one", env).returns mod - mod.expects(:exist?).returns false - - env.module("one").should be_nil - end - - it "should be able to return its modules" do - Puppet::Node::Environment.new("testing").should respond_to(:modules) - end - - describe ".modules" do - it "should return a module named for every directory in each module path" do - env = Puppet::Node::Environment.new("testing") - env.expects(:modulepath).at_least_once.returns %w{/a /b} - Dir.expects(:entries).with("/a").returns %w{foo bar} - Dir.expects(:entries).with("/b").returns %w{bee baz} - - env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort - end - - it "should remove duplicates" do - env = Puppet::Node::Environment.new("testing") - env.expects(:modulepath).returns( %w{/a /b} ).at_least_once - Dir.expects(:entries).with("/a").returns %w{foo} - Dir.expects(:entries).with("/b").returns %w{foo} - - env.modules.collect{|mod| mod.name}.sort.should == %w{foo} - end - - it "should ignore invalid modules" do - env = Puppet::Node::Environment.new("testing") - env.stubs(:modulepath).returns %w{/a} - Dir.expects(:entries).with("/a").returns %w{foo bar} - - Puppet::Module.expects(:new).with { |name, env| name == "foo" }.returns mock("foomod", :name => "foo") - Puppet::Module.expects(:new).with { |name, env| name == "bar" }.raises( Puppet::Module::InvalidName, "name is invalid" ) - - env.modules.collect{|mod| mod.name}.sort.should == %w{foo} - end - - it "should create modules with the correct environment" do - env = Puppet::Node::Environment.new("testing") - env.expects(:modulepath).at_least_once.returns %w{/a} - Dir.expects(:entries).with("/a").returns %w{foo} - - env.modules.each {|mod| mod.environment.should == env } - end - - it "should cache the module list" do - env = Puppet::Node::Environment.new("testing") - env.expects(:modulepath).at_least_once.returns %w{/a} - Dir.expects(:entries).once.with("/a").returns %w{foo} - - env.modules - env.modules - end - end - end - - describe Puppet::Node::Environment::Helper do - before do - @helper = Object.new - @helper.extend(Puppet::Node::Environment::Helper) - end - - it "should be able to set and retrieve the environment" do - @helper.environment = :foo - @helper.environment.name.should == :foo - end - - it "should accept an environment directly" do - env = Puppet::Node::Environment.new :foo - @helper.environment = env - @helper.environment.name.should == :foo - end - - it "should accept an environment as a string" do - env = Puppet::Node::Environment.new "foo" - @helper.environment = env - @helper.environment.name.should == :foo - end - end -end diff --git a/spec/unit/node/facts_spec.rb b/spec/unit/node/facts_spec.rb new file mode 100755 index 000000000..43532cc53 --- /dev/null +++ b/spec/unit/node/facts_spec.rb @@ -0,0 +1,102 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/node/facts' + +describe Puppet::Node::Facts, "when indirecting" do + before do + @facts = Puppet::Node::Facts.new("me") + end + + it "should be able to convert all fact values to strings" do + @facts.values["one"] = 1 + @facts.stringify + @facts.values["one"].should == "1" + end + + it "should add the node's certificate name as the 'clientcert' fact when adding local facts" do + @facts.add_local_facts + @facts.values["clientcert"].should == Puppet.settings[:certname] + end + + it "should add the Puppet version as a 'clientversion' fact when adding local facts" do + @facts.add_local_facts + @facts.values["clientversion"].should == Puppet.version.to_s + end + + it "should add the current environment as a fact if one is not set when adding local facts" do + @facts.add_local_facts + @facts.values["environment"].should == Puppet[:environment] + end + + it "should not replace any existing environment fact when adding local facts" do + @facts.values["environment"] = "foo" + @facts.add_local_facts + @facts.values["environment"].should == "foo" + end + + it "should be able to downcase fact values" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:downcasefacts).returns true + + @facts.values["one"] = "Two" + + @facts.downcase_if_necessary + @facts.values["one"].should == "two" + end + + it "should only try to downcase strings" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:downcasefacts).returns true + + @facts.values["now"] = Time.now + + @facts.downcase_if_necessary + @facts.values["now"].should be_instance_of(Time) + end + + it "should not downcase facts if not configured to do so" do + Puppet.settings.stubs(:value).returns "eh" + Puppet.settings.expects(:value).with(:downcasefacts).returns false + + @facts.values["one"] = "Two" + @facts.downcase_if_necessary + @facts.values["one"].should == "Two" + end + + describe "when indirecting" do + before do + @indirection = stub 'indirection', :request => mock('request'), :name => :facts + + # We have to clear the cache so that the facts ask for our indirection stub, + # instead of anything that might be cached. + Puppet::Util::Cacher.expire + + @facts = Puppet::Node::Facts.new("me", "one" => "two") + end + + it "should redirect to the specified fact store for retrieval" do + Puppet::Node::Facts.stubs(:indirection).returns(@indirection) + @indirection.expects(:find) + Puppet::Node::Facts.find(:my_facts) + end + + it "should redirect to the specified fact store for storage" do + Puppet::Node::Facts.stubs(:indirection).returns(@indirection) + @indirection.expects(:save) + @facts.save + end + + it "should default to the 'facter' terminus" do + Puppet::Node::Facts.indirection.terminus_class.should == :facter + end + end + + describe "when storing and retrieving" do + it "should add metadata to the facts" do + facts = Puppet::Node::Facts.new("me", "one" => "two", "three" => "four") + facts.values[:_timestamp].should be_instance_of(Time) + end + end +end diff --git a/spec/unit/node/facts_spec_spec.rb b/spec/unit/node/facts_spec_spec.rb deleted file mode 100755 index 43532cc53..000000000 --- a/spec/unit/node/facts_spec_spec.rb +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/node/facts' - -describe Puppet::Node::Facts, "when indirecting" do - before do - @facts = Puppet::Node::Facts.new("me") - end - - it "should be able to convert all fact values to strings" do - @facts.values["one"] = 1 - @facts.stringify - @facts.values["one"].should == "1" - end - - it "should add the node's certificate name as the 'clientcert' fact when adding local facts" do - @facts.add_local_facts - @facts.values["clientcert"].should == Puppet.settings[:certname] - end - - it "should add the Puppet version as a 'clientversion' fact when adding local facts" do - @facts.add_local_facts - @facts.values["clientversion"].should == Puppet.version.to_s - end - - it "should add the current environment as a fact if one is not set when adding local facts" do - @facts.add_local_facts - @facts.values["environment"].should == Puppet[:environment] - end - - it "should not replace any existing environment fact when adding local facts" do - @facts.values["environment"] = "foo" - @facts.add_local_facts - @facts.values["environment"].should == "foo" - end - - it "should be able to downcase fact values" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:downcasefacts).returns true - - @facts.values["one"] = "Two" - - @facts.downcase_if_necessary - @facts.values["one"].should == "two" - end - - it "should only try to downcase strings" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:downcasefacts).returns true - - @facts.values["now"] = Time.now - - @facts.downcase_if_necessary - @facts.values["now"].should be_instance_of(Time) - end - - it "should not downcase facts if not configured to do so" do - Puppet.settings.stubs(:value).returns "eh" - Puppet.settings.expects(:value).with(:downcasefacts).returns false - - @facts.values["one"] = "Two" - @facts.downcase_if_necessary - @facts.values["one"].should == "Two" - end - - describe "when indirecting" do - before do - @indirection = stub 'indirection', :request => mock('request'), :name => :facts - - # We have to clear the cache so that the facts ask for our indirection stub, - # instead of anything that might be cached. - Puppet::Util::Cacher.expire - - @facts = Puppet::Node::Facts.new("me", "one" => "two") - end - - it "should redirect to the specified fact store for retrieval" do - Puppet::Node::Facts.stubs(:indirection).returns(@indirection) - @indirection.expects(:find) - Puppet::Node::Facts.find(:my_facts) - end - - it "should redirect to the specified fact store for storage" do - Puppet::Node::Facts.stubs(:indirection).returns(@indirection) - @indirection.expects(:save) - @facts.save - end - - it "should default to the 'facter' terminus" do - Puppet::Node::Facts.indirection.terminus_class.should == :facter - end - end - - describe "when storing and retrieving" do - it "should add metadata to the facts" do - facts = Puppet::Node::Facts.new("me", "one" => "two", "three" => "four") - facts.values[:_timestamp].should be_instance_of(Time) - end - end -end diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb new file mode 100755 index 000000000..e0b4530e8 --- /dev/null +++ b/spec/unit/node_spec.rb @@ -0,0 +1,210 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +describe Puppet::Node do + describe "when managing its environment" do + it "should use any set environment" do + Puppet::Node.new("foo", :environment => "bar").environment.name.should == :bar + end + + it "should support providing an actual environment instance" do + Puppet::Node.new("foo", :environment => Puppet::Node::Environment.new(:bar)).environment.name.should == :bar + end + + it "should determine its environment from its parameters if no environment is set" do + Puppet::Node.new("foo", :parameters => {"environment" => :bar}).environment.name.should == :bar + end + + it "should use the default environment if no environment is provided" do + Puppet::Node.new("foo").environment.name.should == Puppet::Node::Environment.new.name + end + + it "should always return an environment instance rather than a string" do + Puppet::Node.new("foo").environment.should be_instance_of(Puppet::Node::Environment) + end + + it "should allow the environment to be set after initialization" do + node = Puppet::Node.new("foo") + node.environment = :bar + node.environment.name.should == :bar + end + + it "should allow its environment to be set by parameters after initialization" do + node = Puppet::Node.new("foo") + node.parameters["environment"] = :bar + node.environment.name.should == :bar + end + end +end + +describe Puppet::Node, "when initializing" do + before do + @node = Puppet::Node.new("testnode") + end + + it "should set the node name" do + @node.name.should == "testnode" + end + + it "should not allow nil node names" do + proc { Puppet::Node.new(nil) }.should raise_error(ArgumentError) + end + + it "should default to an empty parameter hash" do + @node.parameters.should == {} + end + + it "should default to an empty class array" do + @node.classes.should == [] + end + + it "should note its creation time" do + @node.time.should be_instance_of(Time) + end + + it "should accept parameters passed in during initialization" do + params = {"a" => "b"} + @node = Puppet::Node.new("testing", :parameters => params) + @node.parameters.should == params + end + + it "should accept classes passed in during initialization" do + classes = %w{one two} + @node = Puppet::Node.new("testing", :classes => classes) + @node.classes.should == classes + end + + it "should always return classes as an array" do + @node = Puppet::Node.new("testing", :classes => "myclass") + @node.classes.should == ["myclass"] + end +end + +describe Puppet::Node, "when merging facts" do + before do + @node = Puppet::Node.new("testnode") + Puppet::Node::Facts.stubs(:find).with(@node.name).returns(Puppet::Node::Facts.new(@node.name, "one" => "c", "two" => "b")) + end + + it "should fail intelligently if it cannot find facts" do + Puppet::Node::Facts.expects(:find).with(@node.name).raises "foo" + lambda { @node.fact_merge }.should raise_error(Puppet::Error) + end + + it "should prefer parameters already set on the node over facts from the node" do + @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) + @node.fact_merge + @node.parameters["one"].should == "a" + end + + it "should add passed parameters to the parameter list" do + @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) + @node.fact_merge + @node.parameters["two"].should == "b" + end + + it "should accept arbitrary parameters to merge into its parameters" do + @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) + @node.merge "two" => "three" + @node.parameters["two"].should == "three" + end + + it "should add the environment to the list of parameters" do + Puppet.settings.stubs(:value).with(:environments).returns("one,two") + Puppet.settings.stubs(:value).with(:environment).returns("one") + @node = Puppet::Node.new("testnode", :environment => "one") + @node.merge "two" => "three" + @node.parameters["environment"].should == "one" + end + + it "should not set the environment if it is already set in the parameters" do + Puppet.settings.stubs(:value).with(:environments).returns("one,two") + Puppet.settings.stubs(:value).with(:environment).returns("one") + @node = Puppet::Node.new("testnode", :environment => "one") + @node.merge "environment" => "two" + @node.parameters["environment"].should == "two" + end +end + +describe Puppet::Node, "when indirecting" do + it "should redirect to the indirection" do + @indirection = stub 'indirection', :name => :node + Puppet::Node.stubs(:indirection).returns(@indirection) + @indirection.expects(:find) + Puppet::Node.find(:my_node.to_s) + end + + it "should default to the 'plain' node terminus" do + Puppet::Node.indirection.terminus_class.should == :plain + end + + it "should not have a cache class defined" do + Puppet::Node.indirection.cache_class.should be_nil + end + + after do + Puppet::Util::Cacher.expire + end +end + +describe Puppet::Node, "when generating the list of names to search through" do + before do + @node = Puppet::Node.new("foo.domain.com", :parameters => {"hostname" => "yay", "domain" => "domain.com"}) + end + + it "should return an array of names" do + @node.names.should be_instance_of(Array) + end + + describe "and the node name is fully qualified" do + it "should contain an entry for each part of the node name" do + @node.names.should be_include("foo.domain.com") + @node.names.should be_include("foo.domain") + @node.names.should be_include("foo") + end + end + + it "should include the node's fqdn" do + @node.names.should be_include("yay.domain.com") + end + + it "should combine and include the node's hostname and domain if no fqdn is available" do + @node.names.should be_include("yay.domain.com") + end + + it "should contain an entry for each name available by stripping a segment of the fqdn" do + @node.parameters["fqdn"] = "foo.deep.sub.domain.com" + @node.names.should be_include("foo.deep.sub.domain") + @node.names.should be_include("foo.deep.sub") + end + + describe "and :node_name is set to 'cert'" do + before do + Puppet.settings.stubs(:value).with(:strict_hostname_checking).returns false + Puppet.settings.stubs(:value).with(:node_name).returns "cert" + end + + it "should use the passed-in key as the first value" do + @node.names[0].should == "foo.domain.com" + end + + describe "and strict hostname checking is enabled" do + it "should only use the passed-in key" do + Puppet.settings.expects(:value).with(:strict_hostname_checking).returns true + @node.names.should == ["foo.domain.com"] + end + end + end + + describe "and :node_name is set to 'facter'" do + before do + Puppet.settings.stubs(:value).with(:strict_hostname_checking).returns false + Puppet.settings.stubs(:value).with(:node_name).returns "facter" + end + + it "should use the node's 'hostname' fact as the first value" do + @node.names[0].should == "yay" + end + end +end diff --git a/spec/unit/node_spec_spec.rb b/spec/unit/node_spec_spec.rb deleted file mode 100755 index e0b4530e8..000000000 --- a/spec/unit/node_spec_spec.rb +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -describe Puppet::Node do - describe "when managing its environment" do - it "should use any set environment" do - Puppet::Node.new("foo", :environment => "bar").environment.name.should == :bar - end - - it "should support providing an actual environment instance" do - Puppet::Node.new("foo", :environment => Puppet::Node::Environment.new(:bar)).environment.name.should == :bar - end - - it "should determine its environment from its parameters if no environment is set" do - Puppet::Node.new("foo", :parameters => {"environment" => :bar}).environment.name.should == :bar - end - - it "should use the default environment if no environment is provided" do - Puppet::Node.new("foo").environment.name.should == Puppet::Node::Environment.new.name - end - - it "should always return an environment instance rather than a string" do - Puppet::Node.new("foo").environment.should be_instance_of(Puppet::Node::Environment) - end - - it "should allow the environment to be set after initialization" do - node = Puppet::Node.new("foo") - node.environment = :bar - node.environment.name.should == :bar - end - - it "should allow its environment to be set by parameters after initialization" do - node = Puppet::Node.new("foo") - node.parameters["environment"] = :bar - node.environment.name.should == :bar - end - end -end - -describe Puppet::Node, "when initializing" do - before do - @node = Puppet::Node.new("testnode") - end - - it "should set the node name" do - @node.name.should == "testnode" - end - - it "should not allow nil node names" do - proc { Puppet::Node.new(nil) }.should raise_error(ArgumentError) - end - - it "should default to an empty parameter hash" do - @node.parameters.should == {} - end - - it "should default to an empty class array" do - @node.classes.should == [] - end - - it "should note its creation time" do - @node.time.should be_instance_of(Time) - end - - it "should accept parameters passed in during initialization" do - params = {"a" => "b"} - @node = Puppet::Node.new("testing", :parameters => params) - @node.parameters.should == params - end - - it "should accept classes passed in during initialization" do - classes = %w{one two} - @node = Puppet::Node.new("testing", :classes => classes) - @node.classes.should == classes - end - - it "should always return classes as an array" do - @node = Puppet::Node.new("testing", :classes => "myclass") - @node.classes.should == ["myclass"] - end -end - -describe Puppet::Node, "when merging facts" do - before do - @node = Puppet::Node.new("testnode") - Puppet::Node::Facts.stubs(:find).with(@node.name).returns(Puppet::Node::Facts.new(@node.name, "one" => "c", "two" => "b")) - end - - it "should fail intelligently if it cannot find facts" do - Puppet::Node::Facts.expects(:find).with(@node.name).raises "foo" - lambda { @node.fact_merge }.should raise_error(Puppet::Error) - end - - it "should prefer parameters already set on the node over facts from the node" do - @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) - @node.fact_merge - @node.parameters["one"].should == "a" - end - - it "should add passed parameters to the parameter list" do - @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) - @node.fact_merge - @node.parameters["two"].should == "b" - end - - it "should accept arbitrary parameters to merge into its parameters" do - @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) - @node.merge "two" => "three" - @node.parameters["two"].should == "three" - end - - it "should add the environment to the list of parameters" do - Puppet.settings.stubs(:value).with(:environments).returns("one,two") - Puppet.settings.stubs(:value).with(:environment).returns("one") - @node = Puppet::Node.new("testnode", :environment => "one") - @node.merge "two" => "three" - @node.parameters["environment"].should == "one" - end - - it "should not set the environment if it is already set in the parameters" do - Puppet.settings.stubs(:value).with(:environments).returns("one,two") - Puppet.settings.stubs(:value).with(:environment).returns("one") - @node = Puppet::Node.new("testnode", :environment => "one") - @node.merge "environment" => "two" - @node.parameters["environment"].should == "two" - end -end - -describe Puppet::Node, "when indirecting" do - it "should redirect to the indirection" do - @indirection = stub 'indirection', :name => :node - Puppet::Node.stubs(:indirection).returns(@indirection) - @indirection.expects(:find) - Puppet::Node.find(:my_node.to_s) - end - - it "should default to the 'plain' node terminus" do - Puppet::Node.indirection.terminus_class.should == :plain - end - - it "should not have a cache class defined" do - Puppet::Node.indirection.cache_class.should be_nil - end - - after do - Puppet::Util::Cacher.expire - end -end - -describe Puppet::Node, "when generating the list of names to search through" do - before do - @node = Puppet::Node.new("foo.domain.com", :parameters => {"hostname" => "yay", "domain" => "domain.com"}) - end - - it "should return an array of names" do - @node.names.should be_instance_of(Array) - end - - describe "and the node name is fully qualified" do - it "should contain an entry for each part of the node name" do - @node.names.should be_include("foo.domain.com") - @node.names.should be_include("foo.domain") - @node.names.should be_include("foo") - end - end - - it "should include the node's fqdn" do - @node.names.should be_include("yay.domain.com") - end - - it "should combine and include the node's hostname and domain if no fqdn is available" do - @node.names.should be_include("yay.domain.com") - end - - it "should contain an entry for each name available by stripping a segment of the fqdn" do - @node.parameters["fqdn"] = "foo.deep.sub.domain.com" - @node.names.should be_include("foo.deep.sub.domain") - @node.names.should be_include("foo.deep.sub") - end - - describe "and :node_name is set to 'cert'" do - before do - Puppet.settings.stubs(:value).with(:strict_hostname_checking).returns false - Puppet.settings.stubs(:value).with(:node_name).returns "cert" - end - - it "should use the passed-in key as the first value" do - @node.names[0].should == "foo.domain.com" - end - - describe "and strict hostname checking is enabled" do - it "should only use the passed-in key" do - Puppet.settings.expects(:value).with(:strict_hostname_checking).returns true - @node.names.should == ["foo.domain.com"] - end - end - end - - describe "and :node_name is set to 'facter'" do - before do - Puppet.settings.stubs(:value).with(:strict_hostname_checking).returns false - Puppet.settings.stubs(:value).with(:node_name).returns "facter" - end - - it "should use the node's 'hostname' fact as the first value" do - @node.names[0].should == "yay" - end - end -end diff --git a/spec/unit/other/selinux_spec.rb b/spec/unit/other/selinux_spec.rb new file mode 100644 index 000000000..2287570e7 --- /dev/null +++ b/spec/unit/other/selinux_spec.rb @@ -0,0 +1,85 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/type/selboolean' +require 'puppet/type/selmodule' + +describe Puppet::Type.type(:file), " when manipulating file contexts" do + before :each do + @file = Puppet::Type::File.new( + :name => "/tmp/foo", + :ensure => "file", + :seluser => "user_u", + :selrole => "role_r", + :seltype => "type_t" ) + end + it "should use :seluser to get/set an SELinux user file context attribute" do + @file.property(:seluser).should == "user_u" + end + it "should use :selrole to get/set an SELinux role file context attribute" do + @file.property(:selrole).should == "role_r" + end + it "should use :seltype to get/set an SELinux user file context attribute" do + @file.property(:seltype).should == "type_t" + end +end + +describe Puppet::Type.type(:selboolean), " when manipulating booleans" do + before :each do + provider_class = Puppet::Type::Selboolean.provider(Puppet::Type::Selboolean.providers[0]) + Puppet::Type::Selboolean.stubs(:defaultprovider).returns provider_class + + @bool = Puppet::Type::Selboolean.new( + :name => "foo", + :value => "on", + :persistent => true ) + end + it "should be able to access :name" do + @bool[:name].should == "foo" + end + it "should be able to access :value" do + @bool.property(:value).should == :on + end + it "should set :value to off" do + @bool[:value] = :off + @bool.property(:value).should == :off + end + it "should be able to access :persistent" do + @bool[:persistent].should == :true + end + it "should set :persistent to false" do + @bool[:persistent] = false + @bool[:persistent].should == :false + end +end + +describe Puppet::Type.type(:selmodule), " when checking policy modules" do + before :each do + provider_class = Puppet::Type::Selmodule.provider(Puppet::Type::Selmodule.providers[0]) + Puppet::Type::Selmodule.stubs(:defaultprovider).returns provider_class + + @module = Puppet::Type::Selmodule.new( + :name => "foo", + :selmoduledir => "/some/path", + :selmodulepath => "/some/path/foo.pp", + :syncversion => true) + end + it "should be able to access :name" do + @module[:name].should == "foo" + end + it "should be able to access :selmoduledir" do + @module[:selmoduledir].should == "/some/path" + end + it "should be able to access :selmodulepath" do + @module[:selmodulepath].should == "/some/path/foo.pp" + end + it "should be able to access :syncversion" do + @module.property(:syncversion).should == :true + end + it "should set the syncversion value to false" do + @module[:syncversion] = :false + @module.property(:syncversion).should == :false + end +end + diff --git a/spec/unit/other/selinux_spec_spec.rb b/spec/unit/other/selinux_spec_spec.rb deleted file mode 100644 index 2287570e7..000000000 --- a/spec/unit/other/selinux_spec_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/type/selboolean' -require 'puppet/type/selmodule' - -describe Puppet::Type.type(:file), " when manipulating file contexts" do - before :each do - @file = Puppet::Type::File.new( - :name => "/tmp/foo", - :ensure => "file", - :seluser => "user_u", - :selrole => "role_r", - :seltype => "type_t" ) - end - it "should use :seluser to get/set an SELinux user file context attribute" do - @file.property(:seluser).should == "user_u" - end - it "should use :selrole to get/set an SELinux role file context attribute" do - @file.property(:selrole).should == "role_r" - end - it "should use :seltype to get/set an SELinux user file context attribute" do - @file.property(:seltype).should == "type_t" - end -end - -describe Puppet::Type.type(:selboolean), " when manipulating booleans" do - before :each do - provider_class = Puppet::Type::Selboolean.provider(Puppet::Type::Selboolean.providers[0]) - Puppet::Type::Selboolean.stubs(:defaultprovider).returns provider_class - - @bool = Puppet::Type::Selboolean.new( - :name => "foo", - :value => "on", - :persistent => true ) - end - it "should be able to access :name" do - @bool[:name].should == "foo" - end - it "should be able to access :value" do - @bool.property(:value).should == :on - end - it "should set :value to off" do - @bool[:value] = :off - @bool.property(:value).should == :off - end - it "should be able to access :persistent" do - @bool[:persistent].should == :true - end - it "should set :persistent to false" do - @bool[:persistent] = false - @bool[:persistent].should == :false - end -end - -describe Puppet::Type.type(:selmodule), " when checking policy modules" do - before :each do - provider_class = Puppet::Type::Selmodule.provider(Puppet::Type::Selmodule.providers[0]) - Puppet::Type::Selmodule.stubs(:defaultprovider).returns provider_class - - @module = Puppet::Type::Selmodule.new( - :name => "foo", - :selmoduledir => "/some/path", - :selmodulepath => "/some/path/foo.pp", - :syncversion => true) - end - it "should be able to access :name" do - @module[:name].should == "foo" - end - it "should be able to access :selmoduledir" do - @module[:selmoduledir].should == "/some/path" - end - it "should be able to access :selmodulepath" do - @module[:selmodulepath].should == "/some/path/foo.pp" - end - it "should be able to access :syncversion" do - @module.property(:syncversion).should == :true - end - it "should set the syncversion value to false" do - @module[:syncversion] = :false - @module.property(:syncversion).should == :false - end -end - diff --git a/spec/unit/other/transbucket_spec.rb b/spec/unit/other/transbucket_spec.rb new file mode 100755 index 000000000..a76195120 --- /dev/null +++ b/spec/unit/other/transbucket_spec.rb @@ -0,0 +1,188 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::TransBucket do + before do + @bucket = Puppet::TransBucket.new + end + + it "should be able to produce a RAL component" do + @bucket.name = "luke" + @bucket.type = "foo" + + resource = nil + proc { resource = @bucket.to_ral }.should_not raise_error + resource.should be_instance_of(Puppet::Type::Component) + resource.title.should == "Foo[luke]" + end + + it "should accept TransObjects into its children list" do + object = Puppet::TransObject.new("luke", "user") + proc { @bucket.push(object) }.should_not raise_error + @bucket.each do |o| + o.should equal(object) + end + end + + it "should accept TransBuckets into its children list" do + object = Puppet::TransBucket.new() + proc { @bucket.push(object) }.should_not raise_error + @bucket.each do |o| + o.should equal(object) + end + end + + it "should refuse to accept any children that are not TransObjects or TransBuckets" do + proc { @bucket.push "a test" }.should raise_error + end + + it "should return use 'node' as the type and the provided name as the title if only a type is provided" do + @bucket.type = "mystuff" + @bucket.to_ref.should == "Node[mystuff]" + end + + it "should return use 'component' as the type and the provided type as the title if only a name is provided" do + @bucket.name = "mystuff" + @bucket.to_ref.should == "Class[Mystuff]" + end + + it "should return nil as its reference when type and name are missing" do + @bucket.to_ref.should be_nil + end + + it "should return the title as its reference" do + @bucket.name = "luke" + @bucket.type = "user" + @bucket.to_ref.should == "User[luke]" + end + + it "should canonize resource references when the type is 'component'" do + @bucket.name = 'something' + @bucket.type = 'foo::bar' + + @bucket.to_ref.should == "Foo::Bar[something]" + end +end + +describe Puppet::TransBucket, " when generating a catalog" do + before do + @bottom = Puppet::TransBucket.new + @bottom.type = "fake" + @bottom.name = "bottom" + @bottomobj = Puppet::TransObject.new("bottom", "notify") + @bottom.push @bottomobj + + @middle = Puppet::TransBucket.new + @middle.type = "fake" + @middle.name = "middle" + @middleobj = Puppet::TransObject.new("middle", "notify") + @middle.push(@middleobj) + @middle.push(@bottom) + + @top = Puppet::TransBucket.new + @top.type = "fake" + @top.name = "top" + @topobj = Puppet::TransObject.new("top", "notify") + @top.push(@topobj) + @top.push(@middle) + + @users = %w{top middle bottom} + @fakes = %w{Fake[bottom] Fake[middle] Fake[top]} + end + + it "should convert all transportable objects to RAL resources" do + @catalog = @top.to_catalog + @users.each do |name| + @catalog.vertices.find { |r| r.class.name == :notify and r.title == name }.should be_instance_of(Puppet::Type.type(:notify)) + end + end + + it "should fail if any transportable resources fail to convert to RAL resources" do + @bottomobj.expects(:to_ral).raises ArgumentError + lambda { @bottom.to_catalog }.should raise_error(ArgumentError) + end + + it "should convert all transportable buckets to RAL components" do + @catalog = @top.to_catalog + @fakes.each do |name| + @catalog.vertices.find { |r| r.class.name == :component and r.title == name }.should be_instance_of(Puppet::Type.type(:component)) + end + end + + it "should add all resources to the graph's resource table" do + @catalog = @top.to_catalog + @catalog.resource("fake[top]").should equal(@top) + end + + it "should finalize all resources" do + @catalog = @top.to_catalog + @catalog.vertices.each do |vertex| vertex.should be_finalized end + end + + it "should only call to_ral on each resource once" do + # We just raise exceptions here because we're not interested in + # what happens with the result, only that the method only + # gets called once. + resource = @topobj.to_ral + @topobj.expects(:to_ral).once.returns resource + @top.to_catalog + end + + it "should set each TransObject's catalog before converting to a RAL resource" do + @middleobj.expects(:catalog=).with { |c| c.is_a?(Puppet::Resource::Catalog) } + @top.to_catalog + end + + it "should set each TransBucket's catalog before converting to a RAL resource" do + # each bucket is seen twice in the loop, so we have to handle the case where the config + # is set twice + @bottom.expects(:catalog=).with { |c| c.is_a?(Puppet::Resource::Catalog) }.at_least_once + @top.to_catalog + end +end + +describe Puppet::TransBucket, " when serializing" do + before do + @bucket = Puppet::TransBucket.new(%w{one two}) + @bucket.name = "one" + @bucket.type = "two" + end + + it "should be able to be dumped to yaml" do + proc { YAML.dump(@bucket) }.should_not raise_error + end + + it "should dump YAML that produces an equivalent object" do + result = YAML.dump(@bucket) + + newobj = YAML.load(result) + newobj.name.should == "one" + newobj.type.should == "two" + children = [] + newobj.each do |o| + children << o + end + children.should == %w{one two} + end +end + +describe Puppet::TransBucket, " when converting to a Puppet::Resource" do + before do + @trans = Puppet::TransBucket.new + @trans.name = "foo" + @trans.type = "bar" + @trans.param(:noop, true) + end + + it "should create a resource with the correct type and title" do + result = @trans.to_resource + result.type.should == "Bar" + result.title.should == "foo" + end + + it "should add all of its parameters to the created resource" do + @trans.param(:noop, true) + @trans.to_resource[:noop].should be_true + end +end diff --git a/spec/unit/other/transbucket_spec_spec.rb b/spec/unit/other/transbucket_spec_spec.rb deleted file mode 100755 index a76195120..000000000 --- a/spec/unit/other/transbucket_spec_spec.rb +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::TransBucket do - before do - @bucket = Puppet::TransBucket.new - end - - it "should be able to produce a RAL component" do - @bucket.name = "luke" - @bucket.type = "foo" - - resource = nil - proc { resource = @bucket.to_ral }.should_not raise_error - resource.should be_instance_of(Puppet::Type::Component) - resource.title.should == "Foo[luke]" - end - - it "should accept TransObjects into its children list" do - object = Puppet::TransObject.new("luke", "user") - proc { @bucket.push(object) }.should_not raise_error - @bucket.each do |o| - o.should equal(object) - end - end - - it "should accept TransBuckets into its children list" do - object = Puppet::TransBucket.new() - proc { @bucket.push(object) }.should_not raise_error - @bucket.each do |o| - o.should equal(object) - end - end - - it "should refuse to accept any children that are not TransObjects or TransBuckets" do - proc { @bucket.push "a test" }.should raise_error - end - - it "should return use 'node' as the type and the provided name as the title if only a type is provided" do - @bucket.type = "mystuff" - @bucket.to_ref.should == "Node[mystuff]" - end - - it "should return use 'component' as the type and the provided type as the title if only a name is provided" do - @bucket.name = "mystuff" - @bucket.to_ref.should == "Class[Mystuff]" - end - - it "should return nil as its reference when type and name are missing" do - @bucket.to_ref.should be_nil - end - - it "should return the title as its reference" do - @bucket.name = "luke" - @bucket.type = "user" - @bucket.to_ref.should == "User[luke]" - end - - it "should canonize resource references when the type is 'component'" do - @bucket.name = 'something' - @bucket.type = 'foo::bar' - - @bucket.to_ref.should == "Foo::Bar[something]" - end -end - -describe Puppet::TransBucket, " when generating a catalog" do - before do - @bottom = Puppet::TransBucket.new - @bottom.type = "fake" - @bottom.name = "bottom" - @bottomobj = Puppet::TransObject.new("bottom", "notify") - @bottom.push @bottomobj - - @middle = Puppet::TransBucket.new - @middle.type = "fake" - @middle.name = "middle" - @middleobj = Puppet::TransObject.new("middle", "notify") - @middle.push(@middleobj) - @middle.push(@bottom) - - @top = Puppet::TransBucket.new - @top.type = "fake" - @top.name = "top" - @topobj = Puppet::TransObject.new("top", "notify") - @top.push(@topobj) - @top.push(@middle) - - @users = %w{top middle bottom} - @fakes = %w{Fake[bottom] Fake[middle] Fake[top]} - end - - it "should convert all transportable objects to RAL resources" do - @catalog = @top.to_catalog - @users.each do |name| - @catalog.vertices.find { |r| r.class.name == :notify and r.title == name }.should be_instance_of(Puppet::Type.type(:notify)) - end - end - - it "should fail if any transportable resources fail to convert to RAL resources" do - @bottomobj.expects(:to_ral).raises ArgumentError - lambda { @bottom.to_catalog }.should raise_error(ArgumentError) - end - - it "should convert all transportable buckets to RAL components" do - @catalog = @top.to_catalog - @fakes.each do |name| - @catalog.vertices.find { |r| r.class.name == :component and r.title == name }.should be_instance_of(Puppet::Type.type(:component)) - end - end - - it "should add all resources to the graph's resource table" do - @catalog = @top.to_catalog - @catalog.resource("fake[top]").should equal(@top) - end - - it "should finalize all resources" do - @catalog = @top.to_catalog - @catalog.vertices.each do |vertex| vertex.should be_finalized end - end - - it "should only call to_ral on each resource once" do - # We just raise exceptions here because we're not interested in - # what happens with the result, only that the method only - # gets called once. - resource = @topobj.to_ral - @topobj.expects(:to_ral).once.returns resource - @top.to_catalog - end - - it "should set each TransObject's catalog before converting to a RAL resource" do - @middleobj.expects(:catalog=).with { |c| c.is_a?(Puppet::Resource::Catalog) } - @top.to_catalog - end - - it "should set each TransBucket's catalog before converting to a RAL resource" do - # each bucket is seen twice in the loop, so we have to handle the case where the config - # is set twice - @bottom.expects(:catalog=).with { |c| c.is_a?(Puppet::Resource::Catalog) }.at_least_once - @top.to_catalog - end -end - -describe Puppet::TransBucket, " when serializing" do - before do - @bucket = Puppet::TransBucket.new(%w{one two}) - @bucket.name = "one" - @bucket.type = "two" - end - - it "should be able to be dumped to yaml" do - proc { YAML.dump(@bucket) }.should_not raise_error - end - - it "should dump YAML that produces an equivalent object" do - result = YAML.dump(@bucket) - - newobj = YAML.load(result) - newobj.name.should == "one" - newobj.type.should == "two" - children = [] - newobj.each do |o| - children << o - end - children.should == %w{one two} - end -end - -describe Puppet::TransBucket, " when converting to a Puppet::Resource" do - before do - @trans = Puppet::TransBucket.new - @trans.name = "foo" - @trans.type = "bar" - @trans.param(:noop, true) - end - - it "should create a resource with the correct type and title" do - result = @trans.to_resource - result.type.should == "Bar" - result.title.should == "foo" - end - - it "should add all of its parameters to the created resource" do - @trans.param(:noop, true) - @trans.to_resource[:noop].should be_true - end -end diff --git a/spec/unit/other/transobject_spec.rb b/spec/unit/other/transobject_spec.rb new file mode 100755 index 000000000..92ed2662f --- /dev/null +++ b/spec/unit/other/transobject_spec.rb @@ -0,0 +1,112 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/transportable' + +describe Puppet::TransObject do + it "should canonize resource references" do + resource = Puppet::TransObject.new("me", "foo::bar") + resource.ref.should == 'Foo::Bar[me]' + end + + it "should lower-case resource types for backward compatibility with 0.23.2" do + resource = Puppet::TransObject.new("me", "Foo") + resource.type.should == 'foo' + end +end + +describe Puppet::TransObject, " when serializing" do + before do + @resource = Puppet::TransObject.new("/my/file", "file") + @resource["one"] = "test" + @resource["two"] = "other" + end + + it "should be able to be dumped to yaml" do + proc { YAML.dump(@resource) }.should_not raise_error + end + + it "should produce an equivalent yaml object" do + text = YAML.dump(@resource) + + newresource = YAML.load(text) + newresource.name.should == "/my/file" + newresource.type.should == "file" + %w{one two}.each do |param| + newresource[param].should == @resource[param] + end + end +end + +describe Puppet::TransObject, " when converting to a Puppet::Resource" do + before do + @trans = Puppet::TransObject.new("/my/file", "file") + @trans["one"] = "test" + @trans["two"] = "other" + end + + it "should create a resource with the correct type and title" do + result = @trans.to_resource + result.type.should == "File" + result.title.should == "/my/file" + end + + it "should add all of its parameters to the created resource" do + @trans[:noop] = true + @trans.to_resource[:noop].should be_true + end + + it "should copy over the tags" do + @trans.tags = %w{foo bar} + result = @trans.to_resource + result.should be_tagged("foo") + result.should be_tagged("bar") + end +end + +describe Puppet::TransObject, " when converting to a RAL resource" do + before do + @resource = Puppet::TransObject.new("/my/file", "file") + @resource["one"] = "test" + @resource["two"] = "other" + end + + it "should use a Puppet::Resource to create the resource" do + resource = mock 'resource' + @resource.expects(:to_resource).returns resource + resource.expects(:to_ral).returns "myral" + @resource.to_ral.should == "myral" + end +end + +describe Puppet::TransObject, " when converting to a RAL component instance" do + before do + @resource = Puppet::TransObject.new("/my/file", "one::two") + @resource["one"] = "test" + @resource["noop"] = "other" + end + + it "should use a new TransObject whose name is a resource reference of the type and title of the original TransObject" do + Puppet::Type::Component.expects(:new).with { |resource| resource.type == "component" and resource.name == "One::Two[/my/file]" }.returns(:yay) + @resource.to_component.should == :yay + end + + it "should pass the resource parameters on to the newly created TransObject" do + Puppet::Type::Component.expects(:new).with { |resource| resource["noop"] == "other" }.returns(:yay) + @resource.to_component.should == :yay + end + + it "should copy over the catalog" do + @resource.catalog = "mycat" + Puppet::Type::Component.expects(:new).with { |resource| resource.catalog == "mycat" }.returns(:yay) + @resource.to_component + end + + # LAK:FIXME This really isn't the design we want going forward, but it's + # good enough for now. + it "should not pass resource parameters that are not metaparams" do + Puppet::Type::Component.expects(:new).with { |resource| resource["one"].nil? }.returns(:yay) + @resource.to_component.should == :yay + end +end diff --git a/spec/unit/other/transobject_spec_spec.rb b/spec/unit/other/transobject_spec_spec.rb deleted file mode 100755 index 92ed2662f..000000000 --- a/spec/unit/other/transobject_spec_spec.rb +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/transportable' - -describe Puppet::TransObject do - it "should canonize resource references" do - resource = Puppet::TransObject.new("me", "foo::bar") - resource.ref.should == 'Foo::Bar[me]' - end - - it "should lower-case resource types for backward compatibility with 0.23.2" do - resource = Puppet::TransObject.new("me", "Foo") - resource.type.should == 'foo' - end -end - -describe Puppet::TransObject, " when serializing" do - before do - @resource = Puppet::TransObject.new("/my/file", "file") - @resource["one"] = "test" - @resource["two"] = "other" - end - - it "should be able to be dumped to yaml" do - proc { YAML.dump(@resource) }.should_not raise_error - end - - it "should produce an equivalent yaml object" do - text = YAML.dump(@resource) - - newresource = YAML.load(text) - newresource.name.should == "/my/file" - newresource.type.should == "file" - %w{one two}.each do |param| - newresource[param].should == @resource[param] - end - end -end - -describe Puppet::TransObject, " when converting to a Puppet::Resource" do - before do - @trans = Puppet::TransObject.new("/my/file", "file") - @trans["one"] = "test" - @trans["two"] = "other" - end - - it "should create a resource with the correct type and title" do - result = @trans.to_resource - result.type.should == "File" - result.title.should == "/my/file" - end - - it "should add all of its parameters to the created resource" do - @trans[:noop] = true - @trans.to_resource[:noop].should be_true - end - - it "should copy over the tags" do - @trans.tags = %w{foo bar} - result = @trans.to_resource - result.should be_tagged("foo") - result.should be_tagged("bar") - end -end - -describe Puppet::TransObject, " when converting to a RAL resource" do - before do - @resource = Puppet::TransObject.new("/my/file", "file") - @resource["one"] = "test" - @resource["two"] = "other" - end - - it "should use a Puppet::Resource to create the resource" do - resource = mock 'resource' - @resource.expects(:to_resource).returns resource - resource.expects(:to_ral).returns "myral" - @resource.to_ral.should == "myral" - end -end - -describe Puppet::TransObject, " when converting to a RAL component instance" do - before do - @resource = Puppet::TransObject.new("/my/file", "one::two") - @resource["one"] = "test" - @resource["noop"] = "other" - end - - it "should use a new TransObject whose name is a resource reference of the type and title of the original TransObject" do - Puppet::Type::Component.expects(:new).with { |resource| resource.type == "component" and resource.name == "One::Two[/my/file]" }.returns(:yay) - @resource.to_component.should == :yay - end - - it "should pass the resource parameters on to the newly created TransObject" do - Puppet::Type::Component.expects(:new).with { |resource| resource["noop"] == "other" }.returns(:yay) - @resource.to_component.should == :yay - end - - it "should copy over the catalog" do - @resource.catalog = "mycat" - Puppet::Type::Component.expects(:new).with { |resource| resource.catalog == "mycat" }.returns(:yay) - @resource.to_component - end - - # LAK:FIXME This really isn't the design we want going forward, but it's - # good enough for now. - it "should not pass resource parameters that are not metaparams" do - Puppet::Type::Component.expects(:new).with { |resource| resource["one"].nil? }.returns(:yay) - @resource.to_component.should == :yay - end -end diff --git a/spec/unit/parameter/value_collection_spec.rb b/spec/unit/parameter/value_collection_spec.rb new file mode 100755 index 000000000..421e5a2ea --- /dev/null +++ b/spec/unit/parameter/value_collection_spec.rb @@ -0,0 +1,167 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/parameter' + +describe Puppet::Parameter::ValueCollection do + before do + @collection = Puppet::Parameter::ValueCollection.new + end + + it "should have a method for defining new values" do + @collection.should respond_to(:newvalues) + end + + it "should have a method for adding individual values" do + @collection.should respond_to(:newvalue) + end + + it "should be able to retrieve individual values" do + value = @collection.newvalue(:foo) + @collection.value(:foo).should equal(value) + end + + it "should be able to add an individual value with a block" do + @collection.newvalue(:foo) { raise "testing" } + @collection.value(:foo).block.should be_instance_of(Proc) + end + + it "should be able to add values that are empty strings" do + lambda { @collection.newvalue('') }.should_not raise_error + end + + it "should be able to add values that are empty strings" do + value = @collection.newvalue('') + @collection.match?('').should equal(value) + end + + it "should set :call to :none when adding a value with no block" do + value = @collection.newvalue(:foo) + value.call.should == :none + end + + describe "when adding a value with a block" do + it "should set the method name to 'set_' plus the value name" do + value = @collection.newvalue(:myval) { raise "testing" } + value.method.should == "set_myval" + end + end + + it "should be able to add an individual value with options" do + value = @collection.newvalue(:foo, :call => :bar) + value.call.should == :bar + end + + it "should have a method for validating a value" do + @collection.should respond_to(:validate) + end + + it "should have a method for munging a value" do + @collection.should respond_to(:munge) + end + + it "should be able to generate documentation when it has both values and regexes" do + @collection.newvalues :foo, "bar", %r{test} + @collection.doc.should be_instance_of(String) + end + + it "should correctly generate documentation for values" do + @collection.newvalues :foo + @collection.doc.should be_include("Valid values are ``foo``") + end + + it "should correctly generate documentation for regexes" do + @collection.newvalues %r{\w+} + @collection.doc.should be_include("Values can match ``/\\w+/``") + end + + it "should be able to find the first matching value" do + @collection.newvalues :foo, :bar + @collection.match?("foo").should be_instance_of(Puppet::Parameter::Value) + end + + it "should be able to match symbols" do + @collection.newvalues :foo, :bar + @collection.match?(:foo).should be_instance_of(Puppet::Parameter::Value) + end + + it "should be able to match symbols when a regex is provided" do + @collection.newvalues %r{.} + @collection.match?(:foo).should be_instance_of(Puppet::Parameter::Value) + end + + it "should be able to match values using regexes" do + @collection.newvalues %r{.} + @collection.match?("foo").should_not be_nil + end + + it "should prefer value matches to regex matches" do + @collection.newvalues %r{.}, :foo + @collection.match?("foo").name.should == :foo + end + + describe "when validating values" do + it "should do nothing if no values or regexes have been defined" do + @collection.validate("foo") + end + + it "should fail if the value is not a defined value or alias and does not match a regex" do + @collection.newvalues :foo + lambda { @collection.validate("bar") }.should raise_error(ArgumentError) + end + + it "should succeed if the value is one of the defined values" do + @collection.newvalues :foo + lambda { @collection.validate(:foo) }.should_not raise_error(ArgumentError) + end + + it "should succeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do + @collection.newvalues :foo + lambda { @collection.validate("foo") }.should_not raise_error(ArgumentError) + end + + it "should succeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do + @collection.newvalues "foo" + lambda { @collection.validate(:foo) }.should_not raise_error(ArgumentError) + end + + it "should succeed if the value is one of the defined aliases" do + @collection.newvalues :foo + @collection.aliasvalue :bar, :foo + lambda { @collection.validate("bar") }.should_not raise_error(ArgumentError) + end + + it "should succeed if the value matches one of the regexes" do + @collection.newvalues %r{\d} + lambda { @collection.validate("10") }.should_not raise_error(ArgumentError) + end + end + + describe "when munging values" do + it "should do nothing if no values or regexes have been defined" do + @collection.munge("foo").should == "foo" + end + + it "should return return any matching defined values" do + @collection.newvalues :foo, :bar + @collection.munge("foo").should == :foo + end + + it "should return any matching aliases" do + @collection.newvalues :foo + @collection.aliasvalue :bar, :foo + @collection.munge("bar").should == :foo + end + + it "should return the value if it matches a regex" do + @collection.newvalues %r{\w} + @collection.munge("bar").should == "bar" + end + + it "should return the value if no other option is matched" do + @collection.newvalues :foo + @collection.munge("bar").should == "bar" + end + end +end diff --git a/spec/unit/parameter/value_collection_spec_spec.rb b/spec/unit/parameter/value_collection_spec_spec.rb deleted file mode 100755 index 421e5a2ea..000000000 --- a/spec/unit/parameter/value_collection_spec_spec.rb +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/parameter' - -describe Puppet::Parameter::ValueCollection do - before do - @collection = Puppet::Parameter::ValueCollection.new - end - - it "should have a method for defining new values" do - @collection.should respond_to(:newvalues) - end - - it "should have a method for adding individual values" do - @collection.should respond_to(:newvalue) - end - - it "should be able to retrieve individual values" do - value = @collection.newvalue(:foo) - @collection.value(:foo).should equal(value) - end - - it "should be able to add an individual value with a block" do - @collection.newvalue(:foo) { raise "testing" } - @collection.value(:foo).block.should be_instance_of(Proc) - end - - it "should be able to add values that are empty strings" do - lambda { @collection.newvalue('') }.should_not raise_error - end - - it "should be able to add values that are empty strings" do - value = @collection.newvalue('') - @collection.match?('').should equal(value) - end - - it "should set :call to :none when adding a value with no block" do - value = @collection.newvalue(:foo) - value.call.should == :none - end - - describe "when adding a value with a block" do - it "should set the method name to 'set_' plus the value name" do - value = @collection.newvalue(:myval) { raise "testing" } - value.method.should == "set_myval" - end - end - - it "should be able to add an individual value with options" do - value = @collection.newvalue(:foo, :call => :bar) - value.call.should == :bar - end - - it "should have a method for validating a value" do - @collection.should respond_to(:validate) - end - - it "should have a method for munging a value" do - @collection.should respond_to(:munge) - end - - it "should be able to generate documentation when it has both values and regexes" do - @collection.newvalues :foo, "bar", %r{test} - @collection.doc.should be_instance_of(String) - end - - it "should correctly generate documentation for values" do - @collection.newvalues :foo - @collection.doc.should be_include("Valid values are ``foo``") - end - - it "should correctly generate documentation for regexes" do - @collection.newvalues %r{\w+} - @collection.doc.should be_include("Values can match ``/\\w+/``") - end - - it "should be able to find the first matching value" do - @collection.newvalues :foo, :bar - @collection.match?("foo").should be_instance_of(Puppet::Parameter::Value) - end - - it "should be able to match symbols" do - @collection.newvalues :foo, :bar - @collection.match?(:foo).should be_instance_of(Puppet::Parameter::Value) - end - - it "should be able to match symbols when a regex is provided" do - @collection.newvalues %r{.} - @collection.match?(:foo).should be_instance_of(Puppet::Parameter::Value) - end - - it "should be able to match values using regexes" do - @collection.newvalues %r{.} - @collection.match?("foo").should_not be_nil - end - - it "should prefer value matches to regex matches" do - @collection.newvalues %r{.}, :foo - @collection.match?("foo").name.should == :foo - end - - describe "when validating values" do - it "should do nothing if no values or regexes have been defined" do - @collection.validate("foo") - end - - it "should fail if the value is not a defined value or alias and does not match a regex" do - @collection.newvalues :foo - lambda { @collection.validate("bar") }.should raise_error(ArgumentError) - end - - it "should succeed if the value is one of the defined values" do - @collection.newvalues :foo - lambda { @collection.validate(:foo) }.should_not raise_error(ArgumentError) - end - - it "should succeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do - @collection.newvalues :foo - lambda { @collection.validate("foo") }.should_not raise_error(ArgumentError) - end - - it "should succeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do - @collection.newvalues "foo" - lambda { @collection.validate(:foo) }.should_not raise_error(ArgumentError) - end - - it "should succeed if the value is one of the defined aliases" do - @collection.newvalues :foo - @collection.aliasvalue :bar, :foo - lambda { @collection.validate("bar") }.should_not raise_error(ArgumentError) - end - - it "should succeed if the value matches one of the regexes" do - @collection.newvalues %r{\d} - lambda { @collection.validate("10") }.should_not raise_error(ArgumentError) - end - end - - describe "when munging values" do - it "should do nothing if no values or regexes have been defined" do - @collection.munge("foo").should == "foo" - end - - it "should return return any matching defined values" do - @collection.newvalues :foo, :bar - @collection.munge("foo").should == :foo - end - - it "should return any matching aliases" do - @collection.newvalues :foo - @collection.aliasvalue :bar, :foo - @collection.munge("bar").should == :foo - end - - it "should return the value if it matches a regex" do - @collection.newvalues %r{\w} - @collection.munge("bar").should == "bar" - end - - it "should return the value if no other option is matched" do - @collection.newvalues :foo - @collection.munge("bar").should == "bar" - end - end -end diff --git a/spec/unit/parameter/value_spec.rb b/spec/unit/parameter/value_spec.rb new file mode 100755 index 000000000..f6def01dd --- /dev/null +++ b/spec/unit/parameter/value_spec.rb @@ -0,0 +1,88 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/parameter' + +describe Puppet::Parameter::Value do + it "should require a name" do + lambda { Puppet::Parameter::Value.new }.should raise_error(ArgumentError) + end + + it "should set its name" do + Puppet::Parameter::Value.new(:foo).name.should == :foo + end + + it "should support regexes as names" do + lambda { Puppet::Parameter::Value.new(%r{foo}) }.should_not raise_error + end + + it "should mark itself as a regex if its name is a regex" do + Puppet::Parameter::Value.new(%r{foo}).should be_regex + end + + it "should always convert its name to a symbol if it is not a regex" do + Puppet::Parameter::Value.new("foo").name.should == :foo + Puppet::Parameter::Value.new(true).name.should == :true + end + + it "should support adding aliases" do + Puppet::Parameter::Value.new("foo").should respond_to(:alias) + end + + it "should be able to return its aliases" do + value = Puppet::Parameter::Value.new("foo") + value.alias("bar") + value.alias("baz") + value.aliases.should == [:bar, :baz] + end + + [:block, :call, :method, :event, :required_features].each do |attr| + it "should support a #{attr} attribute" do + value = Puppet::Parameter::Value.new("foo") + value.should respond_to(attr.to_s + "=") + value.should respond_to(attr) + end + end + + it "should default to :instead for :call if a block is provided" do + Puppet::Parameter::Value.new("foo").call.should == :instead + end + + it "should always return events as symbols" do + value = Puppet::Parameter::Value.new("foo") + value.event = "foo_test" + value.event.should == :foo_test + end + + describe "when matching" do + describe "a regex" do + it "should return true if the regex matches the value" do + Puppet::Parameter::Value.new(/\w/).should be_match("foo") + end + + it "should return false if the regex does not match the value" do + Puppet::Parameter::Value.new(/\d/).should_not be_match("foo") + end + end + + describe "a non-regex" do + it "should return true if the value, converted to a symbol, matches the name" do + Puppet::Parameter::Value.new("foo").should be_match("foo") + Puppet::Parameter::Value.new(:foo).should be_match(:foo) + Puppet::Parameter::Value.new(:foo).should be_match("foo") + Puppet::Parameter::Value.new("foo").should be_match(:foo) + end + + it "should return false if the value, converted to a symbol, does not match the name" do + Puppet::Parameter::Value.new(:foo).should_not be_match(:bar) + end + + it "should return true if any of its aliases match" do + value = Puppet::Parameter::Value.new("foo") + value.alias("bar") + value.should be_match("bar") + end + end + end +end diff --git a/spec/unit/parameter/value_spec_spec.rb b/spec/unit/parameter/value_spec_spec.rb deleted file mode 100755 index f6def01dd..000000000 --- a/spec/unit/parameter/value_spec_spec.rb +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/parameter' - -describe Puppet::Parameter::Value do - it "should require a name" do - lambda { Puppet::Parameter::Value.new }.should raise_error(ArgumentError) - end - - it "should set its name" do - Puppet::Parameter::Value.new(:foo).name.should == :foo - end - - it "should support regexes as names" do - lambda { Puppet::Parameter::Value.new(%r{foo}) }.should_not raise_error - end - - it "should mark itself as a regex if its name is a regex" do - Puppet::Parameter::Value.new(%r{foo}).should be_regex - end - - it "should always convert its name to a symbol if it is not a regex" do - Puppet::Parameter::Value.new("foo").name.should == :foo - Puppet::Parameter::Value.new(true).name.should == :true - end - - it "should support adding aliases" do - Puppet::Parameter::Value.new("foo").should respond_to(:alias) - end - - it "should be able to return its aliases" do - value = Puppet::Parameter::Value.new("foo") - value.alias("bar") - value.alias("baz") - value.aliases.should == [:bar, :baz] - end - - [:block, :call, :method, :event, :required_features].each do |attr| - it "should support a #{attr} attribute" do - value = Puppet::Parameter::Value.new("foo") - value.should respond_to(attr.to_s + "=") - value.should respond_to(attr) - end - end - - it "should default to :instead for :call if a block is provided" do - Puppet::Parameter::Value.new("foo").call.should == :instead - end - - it "should always return events as symbols" do - value = Puppet::Parameter::Value.new("foo") - value.event = "foo_test" - value.event.should == :foo_test - end - - describe "when matching" do - describe "a regex" do - it "should return true if the regex matches the value" do - Puppet::Parameter::Value.new(/\w/).should be_match("foo") - end - - it "should return false if the regex does not match the value" do - Puppet::Parameter::Value.new(/\d/).should_not be_match("foo") - end - end - - describe "a non-regex" do - it "should return true if the value, converted to a symbol, matches the name" do - Puppet::Parameter::Value.new("foo").should be_match("foo") - Puppet::Parameter::Value.new(:foo).should be_match(:foo) - Puppet::Parameter::Value.new(:foo).should be_match("foo") - Puppet::Parameter::Value.new("foo").should be_match(:foo) - end - - it "should return false if the value, converted to a symbol, does not match the name" do - Puppet::Parameter::Value.new(:foo).should_not be_match(:bar) - end - - it "should return true if any of its aliases match" do - value = Puppet::Parameter::Value.new("foo") - value.alias("bar") - value.should be_match("bar") - end - end - end -end diff --git a/spec/unit/parameter_spec.rb b/spec/unit/parameter_spec.rb new file mode 100755 index 000000000..03674cb88 --- /dev/null +++ b/spec/unit/parameter_spec.rb @@ -0,0 +1,172 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'puppet/parameter' + +describe Puppet::Parameter do + before do + @class = Class.new(Puppet::Parameter) do + @name = :foo + end + @class.initvars + @resource = mock 'resource' + @resource.stub_everything + @parameter = @class.new :resource => @resource + end + + it "should create a value collection" do + @class = Class.new(Puppet::Parameter) + @class.value_collection.should be_nil + @class.initvars + @class.value_collection.should be_instance_of(Puppet::Parameter::ValueCollection) + end + + it "should return its name as a string when converted to a string" do + @parameter.to_s.should == @parameter.name.to_s + end + + it "should be able to use cached attributes" do + Puppet::Parameter.ancestors.should be_include(Puppet::Util::Cacher) + end + + it "should use the resource catalog for expiration" do + catalog = mock 'catalog' + @resource.stubs(:catalog).returns catalog + @parameter.expirer.should equal(catalog) + end + + [:line, :file, :version].each do |data| + it "should return its resource's #{data} as its #{data}" do + @resource.expects(data).returns "foo" + @parameter.send(data).should == "foo" + end + end + + it "should return the resource's tags plus its name as its tags" do + @resource.expects(:tags).returns %w{one two} + @parameter.tags.should == %w{one two foo} + end + + it "should provide source_descriptors" do + @resource.expects(:line).returns 10 + @resource.expects(:file).returns "file" + @resource.expects(:tags).returns %w{one two} + @resource.expects(:version).returns 50 + @parameter.source_descriptors.should == {:tags=>["one", "two", "foo"], :path=>"//foo", :version=>50, :file => "file", :line => 10} + end + + describe "when returning the value" do + it "should return nil if no value is set" do + @parameter.value.should be_nil + end + + it "should validate the value" do + @parameter.expects(:validate).with("foo") + @parameter.value = "foo" + end + + it "should munge the value and use any result as the actual value" do + @parameter.expects(:munge).with("foo").returns "bar" + @parameter.value = "foo" + @parameter.value.should == "bar" + end + + it "should unmunge the value when accessing the actual value" do + @parameter.class.unmunge do |value| value.to_sym end + @parameter.value = "foo" + @parameter.value.should == :foo + end + + it "should return the actual value by default when unmunging" do + @parameter.unmunge("bar").should == "bar" + end + + it "should return any set value" do + @parameter.value = "foo" + @parameter.value.should == "foo" + end + end + + describe "when validating values" do + it "should do nothing if no values or regexes have been defined" do + @parameter.validate("foo") + end + + it "should catch abnormal failures thrown during validation" do + @class.validate { |v| raise "This is broken" } + lambda { @parameter.validate("eh") }.should raise_error(Puppet::DevError) + end + + it "should fail if the value is not a defined value or alias and does not match a regex" do + @class.newvalues :foo + lambda { @parameter.validate("bar") }.should raise_error(Puppet::Error) + end + + it "should succeed if the value is one of the defined values" do + @class.newvalues :foo + lambda { @parameter.validate(:foo) }.should_not raise_error(ArgumentError) + end + + it "should succeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do + @class.newvalues :foo + lambda { @parameter.validate("foo") }.should_not raise_error(ArgumentError) + end + + it "should succeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do + @class.newvalues "foo" + lambda { @parameter.validate(:foo) }.should_not raise_error(ArgumentError) + end + + it "should succeed if the value is one of the defined aliases" do + @class.newvalues :foo + @class.aliasvalue :bar, :foo + lambda { @parameter.validate("bar") }.should_not raise_error(ArgumentError) + end + + it "should succeed if the value matches one of the regexes" do + @class.newvalues %r{\d} + lambda { @parameter.validate("10") }.should_not raise_error(ArgumentError) + end + end + + describe "when munging values" do + it "should do nothing if no values or regexes have been defined" do + @parameter.munge("foo").should == "foo" + end + + it "should catch abnormal failures thrown during munging" do + @class.munge { |v| raise "This is broken" } + lambda { @parameter.munge("eh") }.should raise_error(Puppet::DevError) + end + + it "should return return any matching defined values" do + @class.newvalues :foo, :bar + @parameter.munge("foo").should == :foo + end + + it "should return any matching aliases" do + @class.newvalues :foo + @class.aliasvalue :bar, :foo + @parameter.munge("bar").should == :foo + end + + it "should return the value if it matches a regex" do + @class.newvalues %r{\w} + @parameter.munge("bar").should == "bar" + end + + it "should return the value if no other option is matched" do + @class.newvalues :foo + @parameter.munge("bar").should == "bar" + end + end + + describe "when logging" do + it "should use its resource's log level and the provided message" do + @resource.expects(:[]).with(:loglevel).returns :notice + @parameter.expects(:send_log).with(:notice, "mymessage") + @parameter.log "mymessage" + end + end +end diff --git a/spec/unit/parameter_spec_spec.rb b/spec/unit/parameter_spec_spec.rb deleted file mode 100755 index 03674cb88..000000000 --- a/spec/unit/parameter_spec_spec.rb +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -require 'puppet/parameter' - -describe Puppet::Parameter do - before do - @class = Class.new(Puppet::Parameter) do - @name = :foo - end - @class.initvars - @resource = mock 'resource' - @resource.stub_everything - @parameter = @class.new :resource => @resource - end - - it "should create a value collection" do - @class = Class.new(Puppet::Parameter) - @class.value_collection.should be_nil - @class.initvars - @class.value_collection.should be_instance_of(Puppet::Parameter::ValueCollection) - end - - it "should return its name as a string when converted to a string" do - @parameter.to_s.should == @parameter.name.to_s - end - - it "should be able to use cached attributes" do - Puppet::Parameter.ancestors.should be_include(Puppet::Util::Cacher) - end - - it "should use the resource catalog for expiration" do - catalog = mock 'catalog' - @resource.stubs(:catalog).returns catalog - @parameter.expirer.should equal(catalog) - end - - [:line, :file, :version].each do |data| - it "should return its resource's #{data} as its #{data}" do - @resource.expects(data).returns "foo" - @parameter.send(data).should == "foo" - end - end - - it "should return the resource's tags plus its name as its tags" do - @resource.expects(:tags).returns %w{one two} - @parameter.tags.should == %w{one two foo} - end - - it "should provide source_descriptors" do - @resource.expects(:line).returns 10 - @resource.expects(:file).returns "file" - @resource.expects(:tags).returns %w{one two} - @resource.expects(:version).returns 50 - @parameter.source_descriptors.should == {:tags=>["one", "two", "foo"], :path=>"//foo", :version=>50, :file => "file", :line => 10} - end - - describe "when returning the value" do - it "should return nil if no value is set" do - @parameter.value.should be_nil - end - - it "should validate the value" do - @parameter.expects(:validate).with("foo") - @parameter.value = "foo" - end - - it "should munge the value and use any result as the actual value" do - @parameter.expects(:munge).with("foo").returns "bar" - @parameter.value = "foo" - @parameter.value.should == "bar" - end - - it "should unmunge the value when accessing the actual value" do - @parameter.class.unmunge do |value| value.to_sym end - @parameter.value = "foo" - @parameter.value.should == :foo - end - - it "should return the actual value by default when unmunging" do - @parameter.unmunge("bar").should == "bar" - end - - it "should return any set value" do - @parameter.value = "foo" - @parameter.value.should == "foo" - end - end - - describe "when validating values" do - it "should do nothing if no values or regexes have been defined" do - @parameter.validate("foo") - end - - it "should catch abnormal failures thrown during validation" do - @class.validate { |v| raise "This is broken" } - lambda { @parameter.validate("eh") }.should raise_error(Puppet::DevError) - end - - it "should fail if the value is not a defined value or alias and does not match a regex" do - @class.newvalues :foo - lambda { @parameter.validate("bar") }.should raise_error(Puppet::Error) - end - - it "should succeed if the value is one of the defined values" do - @class.newvalues :foo - lambda { @parameter.validate(:foo) }.should_not raise_error(ArgumentError) - end - - it "should succeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do - @class.newvalues :foo - lambda { @parameter.validate("foo") }.should_not raise_error(ArgumentError) - end - - it "should succeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do - @class.newvalues "foo" - lambda { @parameter.validate(:foo) }.should_not raise_error(ArgumentError) - end - - it "should succeed if the value is one of the defined aliases" do - @class.newvalues :foo - @class.aliasvalue :bar, :foo - lambda { @parameter.validate("bar") }.should_not raise_error(ArgumentError) - end - - it "should succeed if the value matches one of the regexes" do - @class.newvalues %r{\d} - lambda { @parameter.validate("10") }.should_not raise_error(ArgumentError) - end - end - - describe "when munging values" do - it "should do nothing if no values or regexes have been defined" do - @parameter.munge("foo").should == "foo" - end - - it "should catch abnormal failures thrown during munging" do - @class.munge { |v| raise "This is broken" } - lambda { @parameter.munge("eh") }.should raise_error(Puppet::DevError) - end - - it "should return return any matching defined values" do - @class.newvalues :foo, :bar - @parameter.munge("foo").should == :foo - end - - it "should return any matching aliases" do - @class.newvalues :foo - @class.aliasvalue :bar, :foo - @parameter.munge("bar").should == :foo - end - - it "should return the value if it matches a regex" do - @class.newvalues %r{\w} - @parameter.munge("bar").should == "bar" - end - - it "should return the value if no other option is matched" do - @class.newvalues :foo - @parameter.munge("bar").should == "bar" - end - end - - describe "when logging" do - it "should use its resource's log level and the provided message" do - @resource.expects(:[]).with(:loglevel).returns :notice - @parameter.expects(:send_log).with(:notice, "mymessage") - @parameter.log "mymessage" - end - end -end diff --git a/spec/unit/parser/ast/arithmetic_operator_spec.rb b/spec/unit/parser/ast/arithmetic_operator_spec.rb new file mode 100755 index 000000000..ad8d9947b --- /dev/null +++ b/spec/unit/parser/ast/arithmetic_operator_spec.rb @@ -0,0 +1,73 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ArithmeticOperator do + + ast = Puppet::Parser::AST + + before :each do + @scope = Puppet::Parser::Scope.new() + @one = stub 'lval', :safeevaluate => 1 + @two = stub 'rval', :safeevaluate => 2 + end + + it "should evaluate both branches" do + lval = stub "lval" + lval.expects(:safeevaluate).with(@scope).returns(1) + rval = stub "rval" + rval.expects(:safeevaluate).with(@scope).returns(2) + + operator = ast::ArithmeticOperator.new :rval => rval, :operator => "+", :lval => lval + operator.evaluate(@scope) + end + + it "should fail for an unknown operator" do + lambda { operator = ast::ArithmeticOperator.new :lval => @one, :operator => "%", :rval => @two }.should raise_error + end + + it "should call Puppet::Parser::Scope.number?" do + Puppet::Parser::Scope.expects(:number?).with(1).returns(1) + Puppet::Parser::Scope.expects(:number?).with(2).returns(2) + + ast::ArithmeticOperator.new(:lval => @one, :operator => "+", :rval => @two).evaluate(@scope) + end + + + %w{ + - * / << >>}.each do |op| + it "should call ruby Numeric '#{op}'" do + one = stub 'one' + two = stub 'two' + operator = ast::ArithmeticOperator.new :lval => @one, :operator => op, :rval => @two + Puppet::Parser::Scope.stubs(:number?).with(1).returns(one) + Puppet::Parser::Scope.stubs(:number?).with(2).returns(two) + one.expects(:send).with(op,two) + operator.evaluate(@scope) + end + end + + it "should work even with numbers embedded in strings" do + two = stub 'two', :safeevaluate => "2" + one = stub 'one', :safeevaluate => "1" + operator = ast::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one + operator.evaluate(@scope).should == 3 + end + + it "should work even with floats" do + two = stub 'two', :safeevaluate => 2.53 + one = stub 'one', :safeevaluate => 1.80 + operator = ast::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one + operator.evaluate(@scope).should == 4.33 + end + + it "should work for variables too" do + @scope.expects(:lookupvar).with("one", false).returns(1) + @scope.expects(:lookupvar).with("two", false).returns(2) + one = ast::Variable.new( :value => "one" ) + two = ast::Variable.new( :value => "two" ) + + operator = ast::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two + operator.evaluate(@scope).should == 3 + end + +end diff --git a/spec/unit/parser/ast/arithmetic_operator_spec_spec.rb b/spec/unit/parser/ast/arithmetic_operator_spec_spec.rb deleted file mode 100755 index ad8d9947b..000000000 --- a/spec/unit/parser/ast/arithmetic_operator_spec_spec.rb +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::ArithmeticOperator do - - ast = Puppet::Parser::AST - - before :each do - @scope = Puppet::Parser::Scope.new() - @one = stub 'lval', :safeevaluate => 1 - @two = stub 'rval', :safeevaluate => 2 - end - - it "should evaluate both branches" do - lval = stub "lval" - lval.expects(:safeevaluate).with(@scope).returns(1) - rval = stub "rval" - rval.expects(:safeevaluate).with(@scope).returns(2) - - operator = ast::ArithmeticOperator.new :rval => rval, :operator => "+", :lval => lval - operator.evaluate(@scope) - end - - it "should fail for an unknown operator" do - lambda { operator = ast::ArithmeticOperator.new :lval => @one, :operator => "%", :rval => @two }.should raise_error - end - - it "should call Puppet::Parser::Scope.number?" do - Puppet::Parser::Scope.expects(:number?).with(1).returns(1) - Puppet::Parser::Scope.expects(:number?).with(2).returns(2) - - ast::ArithmeticOperator.new(:lval => @one, :operator => "+", :rval => @two).evaluate(@scope) - end - - - %w{ + - * / << >>}.each do |op| - it "should call ruby Numeric '#{op}'" do - one = stub 'one' - two = stub 'two' - operator = ast::ArithmeticOperator.new :lval => @one, :operator => op, :rval => @two - Puppet::Parser::Scope.stubs(:number?).with(1).returns(one) - Puppet::Parser::Scope.stubs(:number?).with(2).returns(two) - one.expects(:send).with(op,two) - operator.evaluate(@scope) - end - end - - it "should work even with numbers embedded in strings" do - two = stub 'two', :safeevaluate => "2" - one = stub 'one', :safeevaluate => "1" - operator = ast::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one - operator.evaluate(@scope).should == 3 - end - - it "should work even with floats" do - two = stub 'two', :safeevaluate => 2.53 - one = stub 'one', :safeevaluate => 1.80 - operator = ast::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one - operator.evaluate(@scope).should == 4.33 - end - - it "should work for variables too" do - @scope.expects(:lookupvar).with("one", false).returns(1) - @scope.expects(:lookupvar).with("two", false).returns(2) - one = ast::Variable.new( :value => "one" ) - two = ast::Variable.new( :value => "two" ) - - operator = ast::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two - operator.evaluate(@scope).should == 3 - end - -end diff --git a/spec/unit/parser/ast/astarray_spec.rb b/spec/unit/parser/ast/astarray_spec.rb new file mode 100755 index 000000000..1791c711c --- /dev/null +++ b/spec/unit/parser/ast/astarray_spec.rb @@ -0,0 +1,72 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ASTArray do + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should have a [] accessor" do + array = Puppet::Parser::AST::ASTArray.new :children => [] + array.should respond_to(:[]) + end + + it "should evaluate all its children" do + item1 = stub "item1", :is_a? => true + item2 = stub "item2", :is_a? => true + + item1.expects(:safeevaluate).with(@scope).returns(123) + item2.expects(:safeevaluate).with(@scope).returns(246) + + operator = Puppet::Parser::AST::ASTArray.new :children => [item1,item2] + operator.evaluate(@scope) + end + + it "should evaluate childrens of type ASTArray" do + item1 = stub "item1", :is_a? => true + item2 = stub "item2" + item2.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) + item2.stubs(:instance_of?).with(Puppet::Parser::AST::ASTArray).returns(true) + item2.stubs(:each).yields(item1) + + item1.expects(:safeevaluate).with(@scope).returns(123) + + operator = Puppet::Parser::AST::ASTArray.new :children => [item2] + operator.evaluate(@scope).should == [123] + end + + it "should flatten children coming from children ASTArray" do + item1 = stub "item1", :is_a? => true + item2 = stub "item2" + item2.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) + item2.stubs(:instance_of?).with(Puppet::Parser::AST::ASTArray).returns(true) + item2.stubs(:each).yields([item1]) + + item1.expects(:safeevaluate).with(@scope).returns(123) + + operator = Puppet::Parser::AST::ASTArray.new :children => [item2] + operator.evaluate(@scope).should == [123] + end + + it "should not flatten the results of children evaluation" do + item1 = stub "item1", :is_a? => true + item2 = stub "item2" + item2.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) + item2.stubs(:instance_of?).with(Puppet::Parser::AST::ASTArray).returns(true) + item2.stubs(:each).yields([item1]) + + item1.expects(:safeevaluate).with(@scope).returns([123]) + + operator = Puppet::Parser::AST::ASTArray.new :children => [item2] + operator.evaluate(@scope).should == [[123]] + end + + it "should return a valid string with to_s" do + a = stub 'a', :is_a? => true, :to_s => "a" + b = stub 'b', :is_a? => true, :to_s => "b" + array = Puppet::Parser::AST::ASTArray.new :children => [a,b] + + array.to_s.should == "[a, b]" + end +end diff --git a/spec/unit/parser/ast/astarray_spec_spec.rb b/spec/unit/parser/ast/astarray_spec_spec.rb deleted file mode 100755 index 1791c711c..000000000 --- a/spec/unit/parser/ast/astarray_spec_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::ASTArray do - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should have a [] accessor" do - array = Puppet::Parser::AST::ASTArray.new :children => [] - array.should respond_to(:[]) - end - - it "should evaluate all its children" do - item1 = stub "item1", :is_a? => true - item2 = stub "item2", :is_a? => true - - item1.expects(:safeevaluate).with(@scope).returns(123) - item2.expects(:safeevaluate).with(@scope).returns(246) - - operator = Puppet::Parser::AST::ASTArray.new :children => [item1,item2] - operator.evaluate(@scope) - end - - it "should evaluate childrens of type ASTArray" do - item1 = stub "item1", :is_a? => true - item2 = stub "item2" - item2.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) - item2.stubs(:instance_of?).with(Puppet::Parser::AST::ASTArray).returns(true) - item2.stubs(:each).yields(item1) - - item1.expects(:safeevaluate).with(@scope).returns(123) - - operator = Puppet::Parser::AST::ASTArray.new :children => [item2] - operator.evaluate(@scope).should == [123] - end - - it "should flatten children coming from children ASTArray" do - item1 = stub "item1", :is_a? => true - item2 = stub "item2" - item2.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) - item2.stubs(:instance_of?).with(Puppet::Parser::AST::ASTArray).returns(true) - item2.stubs(:each).yields([item1]) - - item1.expects(:safeevaluate).with(@scope).returns(123) - - operator = Puppet::Parser::AST::ASTArray.new :children => [item2] - operator.evaluate(@scope).should == [123] - end - - it "should not flatten the results of children evaluation" do - item1 = stub "item1", :is_a? => true - item2 = stub "item2" - item2.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) - item2.stubs(:instance_of?).with(Puppet::Parser::AST::ASTArray).returns(true) - item2.stubs(:each).yields([item1]) - - item1.expects(:safeevaluate).with(@scope).returns([123]) - - operator = Puppet::Parser::AST::ASTArray.new :children => [item2] - operator.evaluate(@scope).should == [[123]] - end - - it "should return a valid string with to_s" do - a = stub 'a', :is_a? => true, :to_s => "a" - b = stub 'b', :is_a? => true, :to_s => "b" - array = Puppet::Parser::AST::ASTArray.new :children => [a,b] - - array.to_s.should == "[a, b]" - end -end diff --git a/spec/unit/parser/ast/asthash_spec.rb b/spec/unit/parser/ast/asthash_spec.rb new file mode 100644 index 000000000..fc8e1c7ea --- /dev/null +++ b/spec/unit/parser/ast/asthash_spec.rb @@ -0,0 +1,98 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ASTHash do + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should have a merge functionality" do + hash = Puppet::Parser::AST::ASTHash.new(:value => {}) + hash.should respond_to(:merge) + end + + it "should be able to merge 2 AST hashes" do + hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b" }) + + hash.merge(Puppet::Parser::AST::ASTHash.new(:value => {"c" => "d"})) + + hash.value.should == { "a" => "b", "c" => "d" } + end + + it "should be able to merge with a ruby Hash" do + hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b" }) + + hash.merge({"c" => "d"}) + + hash.value.should == { "a" => "b", "c" => "d" } + end + + it "should evaluate each hash value" do + key1 = stub "key1" + value1 = stub "value1" + key2 = stub "key2" + value2 = stub "value2" + + value1.expects(:safeevaluate).with(@scope).returns("b") + value2.expects(:safeevaluate).with(@scope).returns("d") + + operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) + operator.evaluate(@scope) + end + + it "should evaluate the hash keys if they are AST instances" do + key1 = stub "key1" + value1 = stub "value1", :safeevaluate => "one" + key2 = stub "key2" + value2 = stub "value2", :safeevaluate => "two" + + key1.expects(:safeevaluate).with(@scope).returns("1") + key2.expects(:safeevaluate).with(@scope).returns("2") + + operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) + hash = operator.evaluate(@scope) + hash["1"].should == "one" + hash["2"].should == "two" + end + + it "should evaluate the hash keys if they are not AST instances" do + key1 = "1" + value1 = stub "value1", :safeevaluate => "one" + key2 = "2" + value2 = stub "value2", :safeevaluate => "two" + + operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) + hash = operator.evaluate(@scope) + hash["1"].should == "one" + hash["2"].should == "two" + end + + it "should return an evaluated hash" do + key1 = stub "key1" + value1 = stub "value1", :safeevaluate => "b" + key2 = stub "key2" + value2 = stub "value2", :safeevaluate => "d" + + operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) + operator.evaluate(@scope).should == { key1 => "b", key2 => "d" } + end + + describe "when being initialized without arguments" do + it "should evaluate to an empty hash" do + hash = Puppet::Parser::AST::ASTHash.new({}) + hash.evaluate(@scope).should == {} + end + + it "should support merging" do + hash = Puppet::Parser::AST::ASTHash.new({}) + hash.merge({"a" => "b"}).should == {"a" => "b"} + end + end + + it "should return a valid string with to_s" do + hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b", "c" => "d" }) + + hash.to_s.should == '{a => b, c => d}' + end +end diff --git a/spec/unit/parser/ast/asthash_spec_spec.rb b/spec/unit/parser/ast/asthash_spec_spec.rb deleted file mode 100644 index fc8e1c7ea..000000000 --- a/spec/unit/parser/ast/asthash_spec_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::ASTHash do - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should have a merge functionality" do - hash = Puppet::Parser::AST::ASTHash.new(:value => {}) - hash.should respond_to(:merge) - end - - it "should be able to merge 2 AST hashes" do - hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b" }) - - hash.merge(Puppet::Parser::AST::ASTHash.new(:value => {"c" => "d"})) - - hash.value.should == { "a" => "b", "c" => "d" } - end - - it "should be able to merge with a ruby Hash" do - hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b" }) - - hash.merge({"c" => "d"}) - - hash.value.should == { "a" => "b", "c" => "d" } - end - - it "should evaluate each hash value" do - key1 = stub "key1" - value1 = stub "value1" - key2 = stub "key2" - value2 = stub "value2" - - value1.expects(:safeevaluate).with(@scope).returns("b") - value2.expects(:safeevaluate).with(@scope).returns("d") - - operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) - operator.evaluate(@scope) - end - - it "should evaluate the hash keys if they are AST instances" do - key1 = stub "key1" - value1 = stub "value1", :safeevaluate => "one" - key2 = stub "key2" - value2 = stub "value2", :safeevaluate => "two" - - key1.expects(:safeevaluate).with(@scope).returns("1") - key2.expects(:safeevaluate).with(@scope).returns("2") - - operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) - hash = operator.evaluate(@scope) - hash["1"].should == "one" - hash["2"].should == "two" - end - - it "should evaluate the hash keys if they are not AST instances" do - key1 = "1" - value1 = stub "value1", :safeevaluate => "one" - key2 = "2" - value2 = stub "value2", :safeevaluate => "two" - - operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) - hash = operator.evaluate(@scope) - hash["1"].should == "one" - hash["2"].should == "two" - end - - it "should return an evaluated hash" do - key1 = stub "key1" - value1 = stub "value1", :safeevaluate => "b" - key2 = stub "key2" - value2 = stub "value2", :safeevaluate => "d" - - operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) - operator.evaluate(@scope).should == { key1 => "b", key2 => "d" } - end - - describe "when being initialized without arguments" do - it "should evaluate to an empty hash" do - hash = Puppet::Parser::AST::ASTHash.new({}) - hash.evaluate(@scope).should == {} - end - - it "should support merging" do - hash = Puppet::Parser::AST::ASTHash.new({}) - hash.merge({"a" => "b"}).should == {"a" => "b"} - end - end - - it "should return a valid string with to_s" do - hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b", "c" => "d" }) - - hash.to_s.should == '{a => b, c => d}' - end -end diff --git a/spec/unit/parser/ast/boolean_operator_spec.rb b/spec/unit/parser/ast/boolean_operator_spec.rb new file mode 100755 index 000000000..d8e9603e8 --- /dev/null +++ b/spec/unit/parser/ast/boolean_operator_spec.rb @@ -0,0 +1,53 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::BooleanOperator do + + ast = Puppet::Parser::AST + + before :each do + @scope = Puppet::Parser::Scope.new() + @true_ast = ast::Boolean.new( :value => true) + @false_ast = ast::Boolean.new( :value => false) + end + + it "should evaluate left operand inconditionally" do + lval = stub "lval" + lval.expects(:safeevaluate).with(@scope).returns("true") + rval = stub "rval", :safeevaluate => false + rval.expects(:safeevaluate).never + + operator = ast::BooleanOperator.new :rval => rval, :operator => "or", :lval => lval + operator.evaluate(@scope) + end + + it "should evaluate right 'and' operand only if left operand is true" do + lval = stub "lval", :safeevaluate => true + rval = stub "rval", :safeevaluate => false + rval.expects(:safeevaluate).with(@scope).returns(false) + operator = ast::BooleanOperator.new :rval => rval, :operator => "and", :lval => lval + operator.evaluate(@scope) + end + + it "should evaluate right 'or' operand only if left operand is false" do + lval = stub "lval", :safeevaluate => false + rval = stub "rval", :safeevaluate => false + rval.expects(:safeevaluate).with(@scope).returns(false) + operator = ast::BooleanOperator.new :rval => rval, :operator => "or", :lval => lval + operator.evaluate(@scope) + end + + it "should return true for false OR true" do + ast::BooleanOperator.new(:rval => @true_ast, :operator => "or", :lval => @false_ast).evaluate(@scope).should be_true + end + + it "should return false for true AND false" do + ast::BooleanOperator.new(:rval => @true_ast, :operator => "and", :lval => @false_ast ).evaluate(@scope).should be_false + end + + it "should return true for true AND true" do + ast::BooleanOperator.new(:rval => @true_ast, :operator => "and", :lval => @true_ast ).evaluate(@scope).should be_true + end + +end diff --git a/spec/unit/parser/ast/boolean_operator_spec_spec.rb b/spec/unit/parser/ast/boolean_operator_spec_spec.rb deleted file mode 100755 index d8e9603e8..000000000 --- a/spec/unit/parser/ast/boolean_operator_spec_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::BooleanOperator do - - ast = Puppet::Parser::AST - - before :each do - @scope = Puppet::Parser::Scope.new() - @true_ast = ast::Boolean.new( :value => true) - @false_ast = ast::Boolean.new( :value => false) - end - - it "should evaluate left operand inconditionally" do - lval = stub "lval" - lval.expects(:safeevaluate).with(@scope).returns("true") - rval = stub "rval", :safeevaluate => false - rval.expects(:safeevaluate).never - - operator = ast::BooleanOperator.new :rval => rval, :operator => "or", :lval => lval - operator.evaluate(@scope) - end - - it "should evaluate right 'and' operand only if left operand is true" do - lval = stub "lval", :safeevaluate => true - rval = stub "rval", :safeevaluate => false - rval.expects(:safeevaluate).with(@scope).returns(false) - operator = ast::BooleanOperator.new :rval => rval, :operator => "and", :lval => lval - operator.evaluate(@scope) - end - - it "should evaluate right 'or' operand only if left operand is false" do - lval = stub "lval", :safeevaluate => false - rval = stub "rval", :safeevaluate => false - rval.expects(:safeevaluate).with(@scope).returns(false) - operator = ast::BooleanOperator.new :rval => rval, :operator => "or", :lval => lval - operator.evaluate(@scope) - end - - it "should return true for false OR true" do - ast::BooleanOperator.new(:rval => @true_ast, :operator => "or", :lval => @false_ast).evaluate(@scope).should be_true - end - - it "should return false for true AND false" do - ast::BooleanOperator.new(:rval => @true_ast, :operator => "and", :lval => @false_ast ).evaluate(@scope).should be_false - end - - it "should return true for true AND true" do - ast::BooleanOperator.new(:rval => @true_ast, :operator => "and", :lval => @true_ast ).evaluate(@scope).should be_true - end - -end diff --git a/spec/unit/parser/ast/casestatement_spec.rb b/spec/unit/parser/ast/casestatement_spec.rb new file mode 100755 index 000000000..240d3bfea --- /dev/null +++ b/spec/unit/parser/ast/casestatement_spec.rb @@ -0,0 +1,159 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::CaseStatement do + before :each do + @scope = Puppet::Parser::Scope.new() + end + + describe "when evaluating" do + + before :each do + @test = stub 'test' + @test.stubs(:safeevaluate).with(@scope).returns("value") + + @option1 = stub 'option1', :eachopt => nil, :default? => false + @option2 = stub 'option2', :eachopt => nil, :default? => false + + @options = stub 'options' + @options.stubs(:each).multiple_yields(@option1, @option2) + + @casestmt = Puppet::Parser::AST::CaseStatement.new :test => @test, :options => @options + end + + it "should evaluate test" do + @test.expects(:safeevaluate).with(@scope) + + @casestmt.evaluate(@scope) + end + + it "should scan each option" do + @options.expects(:each).multiple_yields(@option1, @option2) + + @casestmt.evaluate(@scope) + end + + describe "when scanning options" do + before :each do + @opval1 = stub_everything 'opval1' + @option1.stubs(:eachopt).yields(@opval1) + + @opval2 = stub_everything 'opval2' + @option2.stubs(:eachopt).yields(@opval2) + end + + it "should evaluate each sub-option" do + @option1.expects(:eachopt) + @option2.expects(:eachopt) + + @casestmt.evaluate(@scope) + end + + it "should evaluate first matching option" do + @opval2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) + @option2.expects(:safeevaluate).with(@scope) + + @casestmt.evaluate(@scope) + end + + it "should return the first matching evaluated option" do + @opval2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) + @option2.stubs(:safeevaluate).with(@scope).returns(:result) + + @casestmt.evaluate(@scope).should == :result + end + + it "should evaluate the default option if none matched" do + @option1.stubs(:default?).returns(true) + @option1.expects(:safeevaluate).with(@scope) + + @casestmt.evaluate(@scope) + end + + it "should return the default evaluated option if none matched" do + @option1.stubs(:default?).returns(true) + @option1.stubs(:safeevaluate).with(@scope).returns(:result) + + @casestmt.evaluate(@scope).should == :result + end + + it "should return nil if nothing matched" do + @casestmt.evaluate(@scope).should be_nil + end + + it "should match and set scope ephemeral variables" do + @opval1.expects(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope } + + @casestmt.evaluate(@scope) + end + + it "should evaluate this regex option if it matches" do + @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) + + @option1.expects(:safeevaluate).with(@scope) + + @casestmt.evaluate(@scope) + end + + it "should return this evaluated regex option if it matches" do + @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) + @option1.stubs(:safeevaluate).with(@scope).returns(:result) + + @casestmt.evaluate(@scope).should == :result + end + + it "should unset scope ephemeral variables after option evaluation" do + @scope.stubs(:ephemeral_level).returns(:level) + @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) + @option1.stubs(:safeevaluate).with(@scope).returns(:result) + + @scope.expects(:unset_ephemeral_var).with(:level) + + @casestmt.evaluate(@scope) + end + + it "should not leak ephemeral variables even if evaluation fails" do + @scope.stubs(:ephemeral_level).returns(:level) + @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) + @option1.stubs(:safeevaluate).with(@scope).raises + + @scope.expects(:unset_ephemeral_var).with(:level) + + lambda { @casestmt.evaluate(@scope) }.should raise_error + end + end + + end + + it "should match if any of the provided options evaluate as true" do + ast = nil + AST = Puppet::Parser::AST + + tests = { + "one" => %w{a b c}, + "two" => %w{e f g} + } + options = tests.collect do |result, values| + values = values.collect { |v| AST::Leaf.new :value => v } + AST::CaseOpt.new(:value => AST::ASTArray.new(:children => values), + :statements => AST::Leaf.new(:value => result)) + end + options << AST::CaseOpt.new(:value => AST::Default.new(:value => "default"), + :statements => AST::Leaf.new(:value => "default")) + + ast = nil + param = AST::Variable.new(:value => "testparam") + ast = AST::CaseStatement.new(:test => param, :options => options) + + tests.each do |should, values| + values.each do |value| + @scope = Puppet::Parser::Scope.new() + @scope.setvar("testparam", value) + result = ast.evaluate(@scope) + + result.should == should + end + end + end +end diff --git a/spec/unit/parser/ast/casestatement_spec_spec.rb b/spec/unit/parser/ast/casestatement_spec_spec.rb deleted file mode 100755 index 240d3bfea..000000000 --- a/spec/unit/parser/ast/casestatement_spec_spec.rb +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::CaseStatement do - before :each do - @scope = Puppet::Parser::Scope.new() - end - - describe "when evaluating" do - - before :each do - @test = stub 'test' - @test.stubs(:safeevaluate).with(@scope).returns("value") - - @option1 = stub 'option1', :eachopt => nil, :default? => false - @option2 = stub 'option2', :eachopt => nil, :default? => false - - @options = stub 'options' - @options.stubs(:each).multiple_yields(@option1, @option2) - - @casestmt = Puppet::Parser::AST::CaseStatement.new :test => @test, :options => @options - end - - it "should evaluate test" do - @test.expects(:safeevaluate).with(@scope) - - @casestmt.evaluate(@scope) - end - - it "should scan each option" do - @options.expects(:each).multiple_yields(@option1, @option2) - - @casestmt.evaluate(@scope) - end - - describe "when scanning options" do - before :each do - @opval1 = stub_everything 'opval1' - @option1.stubs(:eachopt).yields(@opval1) - - @opval2 = stub_everything 'opval2' - @option2.stubs(:eachopt).yields(@opval2) - end - - it "should evaluate each sub-option" do - @option1.expects(:eachopt) - @option2.expects(:eachopt) - - @casestmt.evaluate(@scope) - end - - it "should evaluate first matching option" do - @opval2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) - @option2.expects(:safeevaluate).with(@scope) - - @casestmt.evaluate(@scope) - end - - it "should return the first matching evaluated option" do - @opval2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) - @option2.stubs(:safeevaluate).with(@scope).returns(:result) - - @casestmt.evaluate(@scope).should == :result - end - - it "should evaluate the default option if none matched" do - @option1.stubs(:default?).returns(true) - @option1.expects(:safeevaluate).with(@scope) - - @casestmt.evaluate(@scope) - end - - it "should return the default evaluated option if none matched" do - @option1.stubs(:default?).returns(true) - @option1.stubs(:safeevaluate).with(@scope).returns(:result) - - @casestmt.evaluate(@scope).should == :result - end - - it "should return nil if nothing matched" do - @casestmt.evaluate(@scope).should be_nil - end - - it "should match and set scope ephemeral variables" do - @opval1.expects(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope } - - @casestmt.evaluate(@scope) - end - - it "should evaluate this regex option if it matches" do - @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) - - @option1.expects(:safeevaluate).with(@scope) - - @casestmt.evaluate(@scope) - end - - it "should return this evaluated regex option if it matches" do - @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) - @option1.stubs(:safeevaluate).with(@scope).returns(:result) - - @casestmt.evaluate(@scope).should == :result - end - - it "should unset scope ephemeral variables after option evaluation" do - @scope.stubs(:ephemeral_level).returns(:level) - @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) - @option1.stubs(:safeevaluate).with(@scope).returns(:result) - - @scope.expects(:unset_ephemeral_var).with(:level) - - @casestmt.evaluate(@scope) - end - - it "should not leak ephemeral variables even if evaluation fails" do - @scope.stubs(:ephemeral_level).returns(:level) - @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) - @option1.stubs(:safeevaluate).with(@scope).raises - - @scope.expects(:unset_ephemeral_var).with(:level) - - lambda { @casestmt.evaluate(@scope) }.should raise_error - end - end - - end - - it "should match if any of the provided options evaluate as true" do - ast = nil - AST = Puppet::Parser::AST - - tests = { - "one" => %w{a b c}, - "two" => %w{e f g} - } - options = tests.collect do |result, values| - values = values.collect { |v| AST::Leaf.new :value => v } - AST::CaseOpt.new(:value => AST::ASTArray.new(:children => values), - :statements => AST::Leaf.new(:value => result)) - end - options << AST::CaseOpt.new(:value => AST::Default.new(:value => "default"), - :statements => AST::Leaf.new(:value => "default")) - - ast = nil - param = AST::Variable.new(:value => "testparam") - ast = AST::CaseStatement.new(:test => param, :options => options) - - tests.each do |should, values| - values.each do |value| - @scope = Puppet::Parser::Scope.new() - @scope.setvar("testparam", value) - result = ast.evaluate(@scope) - - result.should == should - end - end - end -end diff --git a/spec/unit/parser/ast/collection_spec.rb b/spec/unit/parser/ast/collection_spec.rb new file mode 100755 index 000000000..c141bd708 --- /dev/null +++ b/spec/unit/parser/ast/collection_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Collection do + before :each do + @scope = stub_everything 'scope' + @compiler = stub_everything 'compile' + @scope.stubs(:compiler).returns(@compiler) + + @overrides = stub_everything 'overrides' + @overrides.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) + + end + + it "should evaluate its query" do + query = mock 'query' + collection = Puppet::Parser::AST::Collection.new :query => query, :form => :virtual + + query.expects(:safeevaluate).with(@scope) + + collection.evaluate(@scope) + end + + it "should instantiate a Collector for this type" do + collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test" + + Puppet::Parser::Collector.expects(:new).with(@scope, "test", nil, nil, :virtual) + + collection.evaluate(@scope) + end + + it "should tell the compiler about this collector" do + collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test" + Puppet::Parser::Collector.stubs(:new).returns("whatever") + + @compiler.expects(:add_collection).with("whatever") + + collection.evaluate(@scope) + end + + it "should evaluate overriden paramaters" do + collector = stub_everything 'collector' + collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test", :override => @overrides + Puppet::Parser::Collector.stubs(:new).returns(collector) + + @overrides.expects(:safeevaluate).with(@scope) + + collection.evaluate(@scope) + end + + it "should tell the collector about overrides" do + collector = mock 'collector' + collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test", :override => @overrides + Puppet::Parser::Collector.stubs(:new).returns(collector) + + collector.expects(:add_override) + + collection.evaluate(@scope) + end + + +end diff --git a/spec/unit/parser/ast/collection_spec_spec.rb b/spec/unit/parser/ast/collection_spec_spec.rb deleted file mode 100755 index c141bd708..000000000 --- a/spec/unit/parser/ast/collection_spec_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::Collection do - before :each do - @scope = stub_everything 'scope' - @compiler = stub_everything 'compile' - @scope.stubs(:compiler).returns(@compiler) - - @overrides = stub_everything 'overrides' - @overrides.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) - - end - - it "should evaluate its query" do - query = mock 'query' - collection = Puppet::Parser::AST::Collection.new :query => query, :form => :virtual - - query.expects(:safeevaluate).with(@scope) - - collection.evaluate(@scope) - end - - it "should instantiate a Collector for this type" do - collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test" - - Puppet::Parser::Collector.expects(:new).with(@scope, "test", nil, nil, :virtual) - - collection.evaluate(@scope) - end - - it "should tell the compiler about this collector" do - collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test" - Puppet::Parser::Collector.stubs(:new).returns("whatever") - - @compiler.expects(:add_collection).with("whatever") - - collection.evaluate(@scope) - end - - it "should evaluate overriden paramaters" do - collector = stub_everything 'collector' - collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test", :override => @overrides - Puppet::Parser::Collector.stubs(:new).returns(collector) - - @overrides.expects(:safeevaluate).with(@scope) - - collection.evaluate(@scope) - end - - it "should tell the collector about overrides" do - collector = mock 'collector' - collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test", :override => @overrides - Puppet::Parser::Collector.stubs(:new).returns(collector) - - collector.expects(:add_override) - - collection.evaluate(@scope) - end - - -end diff --git a/spec/unit/parser/ast/collexpr_spec.rb b/spec/unit/parser/ast/collexpr_spec.rb new file mode 100755 index 000000000..4dfc1e97a --- /dev/null +++ b/spec/unit/parser/ast/collexpr_spec.rb @@ -0,0 +1,115 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::CollExpr do + + ast = Puppet::Parser::AST + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + describe "when evaluating with two operands" do + before :each do + @test1 = mock 'test1' + @test1.expects(:safeevaluate).with(@scope).returns("test1") + @test2 = mock 'test2' + @test2.expects(:safeevaluate).with(@scope).returns("test2") + end + + it "should evaluate both" do + collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==") + collexpr.evaluate(@scope) + end + + it "should produce a textual representation and code of the expression" do + collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==") + result = collexpr.evaluate(@scope) + result[0].should == "param_values.value = 'test2' and param_names.name = 'test1'" + result[1].should be_an_instance_of(Proc) + end + + it "should propagate expression type and form to child if expression themselves" do + [@test1, @test2].each do |t| + t.expects(:is_a?).returns(true) + t.expects(:form).returns(false) + t.expects(:type).returns(false) + t.expects(:type=) + t.expects(:form=) + end + + collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==", :form => true, :type => true) + result = collexpr.evaluate(@scope) + end + + describe "and when evaluating the produced code" do + before :each do + @resource = mock 'resource' + @resource.expects(:[]).with("test1").at_least(1).returns("test2") + end + + it "should evaluate like the original expression for ==" do + collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "==") + collexpr.evaluate(@scope)[1].call(@resource).should === (@resource["test1"] == "test2") + end + + it "should evaluate like the original expression for !=" do + collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "!=") + collexpr.evaluate(@scope)[1].call(@resource).should === (@resource["test1"] != "test2") + end + end + + it "should warn if this is an exported collection containing parenthesis (unsupported)" do + collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==", :parens => true, :form => :exported) + Puppet.expects(:warning) + collexpr.evaluate(@scope) + end + + %w{and or}.each do |op| + it "should raise an error if this is an exported collection with #{op} operator (unsupported)" do + collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=> op, :form => :exported) + lambda { collexpr.evaluate(@scope) }.should raise_error(Puppet::ParseError) + end + end + end + + describe "when evaluating with tags" do + before :each do + @tag = stub 'tag', :safeevaluate => 'tag' + @value = stub 'value', :safeevaluate => 'value' + + @resource = stub 'resource' + @resource.stubs(:tagged?).with("value").returns(true) + end + + it "should produce a textual representation of the expression" do + collexpr = ast::CollExpr.new(:test1 => @tag, :test2 => @value, :oper=>"==") + result = collexpr.evaluate(@scope) + result[0].should == "puppet_tags.name = 'value'" + end + + it "should inspect resource tags if the query term is on tags" do + collexpr = ast::CollExpr.new(:test1 => @tag, :test2 => @value, :oper => "==") + collexpr.evaluate(@scope)[1].call(@resource).should be_true + end + end + + [:exported,:virtual].each do |mode| + it "should check for array member equality if resource parameter is an array for == in mode #{mode}" do + array = mock 'array', :safeevaluate => "array" + test1 = mock 'test1' + test1.expects(:safeevaluate).with(@scope).returns("test1") + + resource = mock 'resource' + resource.expects(:[]).with("array").at_least(1).returns(["test1","test2","test3"]) + collexpr = ast::CollExpr.new(:test1 => array, :test2 => test1, :oper => "==", :form => mode) + collexpr.evaluate(@scope)[1].call(resource).should be_true + end + end + + it "should raise an error for invalid operator" do + lambda { collexpr = ast::CollExpr.new(:oper=>">") }.should raise_error + end + +end diff --git a/spec/unit/parser/ast/collexpr_spec_spec.rb b/spec/unit/parser/ast/collexpr_spec_spec.rb deleted file mode 100755 index 4dfc1e97a..000000000 --- a/spec/unit/parser/ast/collexpr_spec_spec.rb +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::CollExpr do - - ast = Puppet::Parser::AST - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - describe "when evaluating with two operands" do - before :each do - @test1 = mock 'test1' - @test1.expects(:safeevaluate).with(@scope).returns("test1") - @test2 = mock 'test2' - @test2.expects(:safeevaluate).with(@scope).returns("test2") - end - - it "should evaluate both" do - collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==") - collexpr.evaluate(@scope) - end - - it "should produce a textual representation and code of the expression" do - collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==") - result = collexpr.evaluate(@scope) - result[0].should == "param_values.value = 'test2' and param_names.name = 'test1'" - result[1].should be_an_instance_of(Proc) - end - - it "should propagate expression type and form to child if expression themselves" do - [@test1, @test2].each do |t| - t.expects(:is_a?).returns(true) - t.expects(:form).returns(false) - t.expects(:type).returns(false) - t.expects(:type=) - t.expects(:form=) - end - - collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==", :form => true, :type => true) - result = collexpr.evaluate(@scope) - end - - describe "and when evaluating the produced code" do - before :each do - @resource = mock 'resource' - @resource.expects(:[]).with("test1").at_least(1).returns("test2") - end - - it "should evaluate like the original expression for ==" do - collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "==") - collexpr.evaluate(@scope)[1].call(@resource).should === (@resource["test1"] == "test2") - end - - it "should evaluate like the original expression for !=" do - collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "!=") - collexpr.evaluate(@scope)[1].call(@resource).should === (@resource["test1"] != "test2") - end - end - - it "should warn if this is an exported collection containing parenthesis (unsupported)" do - collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==", :parens => true, :form => :exported) - Puppet.expects(:warning) - collexpr.evaluate(@scope) - end - - %w{and or}.each do |op| - it "should raise an error if this is an exported collection with #{op} operator (unsupported)" do - collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=> op, :form => :exported) - lambda { collexpr.evaluate(@scope) }.should raise_error(Puppet::ParseError) - end - end - end - - describe "when evaluating with tags" do - before :each do - @tag = stub 'tag', :safeevaluate => 'tag' - @value = stub 'value', :safeevaluate => 'value' - - @resource = stub 'resource' - @resource.stubs(:tagged?).with("value").returns(true) - end - - it "should produce a textual representation of the expression" do - collexpr = ast::CollExpr.new(:test1 => @tag, :test2 => @value, :oper=>"==") - result = collexpr.evaluate(@scope) - result[0].should == "puppet_tags.name = 'value'" - end - - it "should inspect resource tags if the query term is on tags" do - collexpr = ast::CollExpr.new(:test1 => @tag, :test2 => @value, :oper => "==") - collexpr.evaluate(@scope)[1].call(@resource).should be_true - end - end - - [:exported,:virtual].each do |mode| - it "should check for array member equality if resource parameter is an array for == in mode #{mode}" do - array = mock 'array', :safeevaluate => "array" - test1 = mock 'test1' - test1.expects(:safeevaluate).with(@scope).returns("test1") - - resource = mock 'resource' - resource.expects(:[]).with("array").at_least(1).returns(["test1","test2","test3"]) - collexpr = ast::CollExpr.new(:test1 => array, :test2 => test1, :oper => "==", :form => mode) - collexpr.evaluate(@scope)[1].call(resource).should be_true - end - end - - it "should raise an error for invalid operator" do - lambda { collexpr = ast::CollExpr.new(:oper=>">") }.should raise_error - end - -end diff --git a/spec/unit/parser/ast/comparison_operator_spec.rb b/spec/unit/parser/ast/comparison_operator_spec.rb new file mode 100755 index 000000000..97402433f --- /dev/null +++ b/spec/unit/parser/ast/comparison_operator_spec.rb @@ -0,0 +1,92 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ComparisonOperator do + before :each do + @scope = Puppet::Parser::Scope.new() + @one = stub 'one', :safeevaluate => "1" + @two = stub 'two', :safeevaluate => "2" + end + + it "should evaluate both branches" do + lval = stub "lval" + lval.expects(:safeevaluate).with(@scope) + rval = stub "rval" + rval.expects(:safeevaluate).with(@scope) + + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => lval, :operator => "==", :rval => rval + operator.evaluate(@scope) + end + + it "should convert arguments strings to numbers if they are" do + Puppet::Parser::Scope.expects(:number?).with("1").returns(1) + Puppet::Parser::Scope.expects(:number?).with("2").returns(2) + + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => "==", :rval => @two + operator.evaluate(@scope) + end + + %w{< > <= >= ==}.each do |oper| + it "should use string comparison #{oper} if operands are strings" do + lval = stub 'one', :safeevaluate => "one" + rval = stub 'two', :safeevaluate => "two" + Puppet::Parser::Scope.stubs(:number?).with("one").returns(nil) + Puppet::Parser::Scope.stubs(:number?).with("two").returns(nil) + + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => lval, :operator => oper, :rval => rval + operator.evaluate(@scope).should == "one".send(oper,"two") + end + end + + it "should fail with arguments of different types" do + lval = stub 'one', :safeevaluate => "one" + rval = stub 'two', :safeevaluate => "2" + Puppet::Parser::Scope.stubs(:number?).with("one").returns(nil) + Puppet::Parser::Scope.stubs(:number?).with("2").returns(2) + + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => lval, :operator => ">", :rval => rval + lambda { operator.evaluate(@scope) }.should raise_error(ArgumentError) + end + + it "should fail for an unknown operator" do + lambda { operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => "or", :rval => @two }.should raise_error + end + + %w{< > <= >= ==}.each do |oper| + it "should return the result of using '#{oper}' to compare the left and right sides" do + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => oper, :rval => @two + + operator.evaluate(@scope).should == 1.send(oper,2) + end + end + + it "should return the result of using '!=' to compare the left and right sides" do + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => '!=', :rval => @two + + operator.evaluate(@scope).should == true + end + + it "should work for variables too" do + one = Puppet::Parser::AST::Variable.new( :value => "one" ) + two = Puppet::Parser::AST::Variable.new( :value => "two" ) + + @scope.expects(:lookupvar).with("one", false).returns(1) + @scope.expects(:lookupvar).with("two", false).returns(2) + + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => one, :operator => "<", :rval => two + operator.evaluate(@scope).should == true + end + + # see ticket #1759 + %w{< > <= >=}.each do |oper| + it "should return the correct result of using '#{oper}' to compare 10 and 9" do + ten = stub 'one', :safeevaluate => "10" + nine = stub 'two', :safeevaluate => "9" + operator = Puppet::Parser::AST::ComparisonOperator.new :lval => ten, :operator => oper, :rval => nine + + operator.evaluate(@scope).should == 10.send(oper,9) + end + end + +end diff --git a/spec/unit/parser/ast/comparison_operator_spec_spec.rb b/spec/unit/parser/ast/comparison_operator_spec_spec.rb deleted file mode 100755 index 97402433f..000000000 --- a/spec/unit/parser/ast/comparison_operator_spec_spec.rb +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::ComparisonOperator do - before :each do - @scope = Puppet::Parser::Scope.new() - @one = stub 'one', :safeevaluate => "1" - @two = stub 'two', :safeevaluate => "2" - end - - it "should evaluate both branches" do - lval = stub "lval" - lval.expects(:safeevaluate).with(@scope) - rval = stub "rval" - rval.expects(:safeevaluate).with(@scope) - - operator = Puppet::Parser::AST::ComparisonOperator.new :lval => lval, :operator => "==", :rval => rval - operator.evaluate(@scope) - end - - it "should convert arguments strings to numbers if they are" do - Puppet::Parser::Scope.expects(:number?).with("1").returns(1) - Puppet::Parser::Scope.expects(:number?).with("2").returns(2) - - operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => "==", :rval => @two - operator.evaluate(@scope) - end - - %w{< > <= >= ==}.each do |oper| - it "should use string comparison #{oper} if operands are strings" do - lval = stub 'one', :safeevaluate => "one" - rval = stub 'two', :safeevaluate => "two" - Puppet::Parser::Scope.stubs(:number?).with("one").returns(nil) - Puppet::Parser::Scope.stubs(:number?).with("two").returns(nil) - - operator = Puppet::Parser::AST::ComparisonOperator.new :lval => lval, :operator => oper, :rval => rval - operator.evaluate(@scope).should == "one".send(oper,"two") - end - end - - it "should fail with arguments of different types" do - lval = stub 'one', :safeevaluate => "one" - rval = stub 'two', :safeevaluate => "2" - Puppet::Parser::Scope.stubs(:number?).with("one").returns(nil) - Puppet::Parser::Scope.stubs(:number?).with("2").returns(2) - - operator = Puppet::Parser::AST::ComparisonOperator.new :lval => lval, :operator => ">", :rval => rval - lambda { operator.evaluate(@scope) }.should raise_error(ArgumentError) - end - - it "should fail for an unknown operator" do - lambda { operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => "or", :rval => @two }.should raise_error - end - - %w{< > <= >= ==}.each do |oper| - it "should return the result of using '#{oper}' to compare the left and right sides" do - operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => oper, :rval => @two - - operator.evaluate(@scope).should == 1.send(oper,2) - end - end - - it "should return the result of using '!=' to compare the left and right sides" do - operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => '!=', :rval => @two - - operator.evaluate(@scope).should == true - end - - it "should work for variables too" do - one = Puppet::Parser::AST::Variable.new( :value => "one" ) - two = Puppet::Parser::AST::Variable.new( :value => "two" ) - - @scope.expects(:lookupvar).with("one", false).returns(1) - @scope.expects(:lookupvar).with("two", false).returns(2) - - operator = Puppet::Parser::AST::ComparisonOperator.new :lval => one, :operator => "<", :rval => two - operator.evaluate(@scope).should == true - end - - # see ticket #1759 - %w{< > <= >=}.each do |oper| - it "should return the correct result of using '#{oper}' to compare 10 and 9" do - ten = stub 'one', :safeevaluate => "10" - nine = stub 'two', :safeevaluate => "9" - operator = Puppet::Parser::AST::ComparisonOperator.new :lval => ten, :operator => oper, :rval => nine - - operator.evaluate(@scope).should == 10.send(oper,9) - end - end - -end diff --git a/spec/unit/parser/ast/function_spec.rb b/spec/unit/parser/ast/function_spec.rb new file mode 100644 index 000000000..bb687eac0 --- /dev/null +++ b/spec/unit/parser/ast/function_spec.rb @@ -0,0 +1,83 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Function do + before :each do + @scope = mock 'scope' + end + + describe "when initializing" do + it "should not fail if the function doesn't exist" do + Puppet::Parser::Functions.stubs(:function).returns(false) + + lambda{ Puppet::Parser::AST::Function.new :name => "dontexist" }.should_not raise_error(Puppet::ParseError) + + end + end + + it "should return its representation with to_s" do + args = stub 'args', :is_a? => true, :to_s => "[a, b]" + + Puppet::Parser::AST::Function.new(:name => "func", :arguments => args).to_s.should == "func(a, b)" + end + + describe "when evaluating" do + + it "should fail if the function doesn't exist" do + Puppet::Parser::Functions.stubs(:function).returns(false) + func = Puppet::Parser::AST::Function.new :name => "dontexist" + + lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError) + end + + it "should fail if the function is a statement used as rvalue" do + Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) + Puppet::Parser::Functions.stubs(:rvalue?).with("exist").returns(false) + + func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :rvalue + + lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError, "Function 'exist' does not return a value") + end + + it "should fail if the function is an rvalue used as statement" do + Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) + Puppet::Parser::Functions.stubs(:rvalue?).with("exist").returns(true) + + func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement + + lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError,"Function 'exist' must be the value of a statement") + end + + it "should evaluate its arguments" do + argument = stub 'arg' + Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) + func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument + @scope.stubs(:function_exist) + + argument.expects(:safeevaluate).with(@scope).returns("argument") + + func.evaluate(@scope) + end + + it "should call the underlying ruby function" do + argument = stub 'arg', :safeevaluate => "nothing" + Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) + func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument + + @scope.expects(:function_exist).with("nothing") + + func.evaluate(@scope) + end + + it "should return the ruby function return for rvalue functions" do + argument = stub 'arg', :safeevaluate => "nothing" + Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) + func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument + @scope.stubs(:function_exist).with("nothing").returns("returning") + + func.evaluate(@scope).should == "returning" + end + + end +end diff --git a/spec/unit/parser/ast/function_spec_spec.rb b/spec/unit/parser/ast/function_spec_spec.rb deleted file mode 100644 index bb687eac0..000000000 --- a/spec/unit/parser/ast/function_spec_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::Function do - before :each do - @scope = mock 'scope' - end - - describe "when initializing" do - it "should not fail if the function doesn't exist" do - Puppet::Parser::Functions.stubs(:function).returns(false) - - lambda{ Puppet::Parser::AST::Function.new :name => "dontexist" }.should_not raise_error(Puppet::ParseError) - - end - end - - it "should return its representation with to_s" do - args = stub 'args', :is_a? => true, :to_s => "[a, b]" - - Puppet::Parser::AST::Function.new(:name => "func", :arguments => args).to_s.should == "func(a, b)" - end - - describe "when evaluating" do - - it "should fail if the function doesn't exist" do - Puppet::Parser::Functions.stubs(:function).returns(false) - func = Puppet::Parser::AST::Function.new :name => "dontexist" - - lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError) - end - - it "should fail if the function is a statement used as rvalue" do - Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) - Puppet::Parser::Functions.stubs(:rvalue?).with("exist").returns(false) - - func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :rvalue - - lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError, "Function 'exist' does not return a value") - end - - it "should fail if the function is an rvalue used as statement" do - Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) - Puppet::Parser::Functions.stubs(:rvalue?).with("exist").returns(true) - - func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement - - lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError,"Function 'exist' must be the value of a statement") - end - - it "should evaluate its arguments" do - argument = stub 'arg' - Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) - func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument - @scope.stubs(:function_exist) - - argument.expects(:safeevaluate).with(@scope).returns("argument") - - func.evaluate(@scope) - end - - it "should call the underlying ruby function" do - argument = stub 'arg', :safeevaluate => "nothing" - Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) - func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument - - @scope.expects(:function_exist).with("nothing") - - func.evaluate(@scope) - end - - it "should return the ruby function return for rvalue functions" do - argument = stub 'arg', :safeevaluate => "nothing" - Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) - func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument - @scope.stubs(:function_exist).with("nothing").returns("returning") - - func.evaluate(@scope).should == "returning" - end - - end -end diff --git a/spec/unit/parser/ast/ifstatement_spec.rb b/spec/unit/parser/ast/ifstatement_spec.rb new file mode 100755 index 000000000..83121cddd --- /dev/null +++ b/spec/unit/parser/ast/ifstatement_spec.rb @@ -0,0 +1,76 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::IfStatement do + before :each do + @scope = Puppet::Parser::Scope.new() + end + + describe "when evaluating" do + + before :each do + @test = stub 'test' + @test.stubs(:safeevaluate).with(@scope) + + @stmt = stub 'stmt' + @stmt.stubs(:safeevaluate).with(@scope) + + @else = stub 'else' + @else.stubs(:safeevaluate).with(@scope) + + @ifstmt = Puppet::Parser::AST::IfStatement.new :test => @test, :statements => @stmt + @ifelsestmt = Puppet::Parser::AST::IfStatement.new :test => @test, :statements => @stmt, :else => @else + end + + it "should evaluate test" do + Puppet::Parser::Scope.stubs(:true?).returns(false) + + @test.expects(:safeevaluate).with(@scope) + + @ifstmt.evaluate(@scope) + end + + it "should evaluate if statements if test is true" do + Puppet::Parser::Scope.stubs(:true?).returns(true) + + @stmt.expects(:safeevaluate).with(@scope) + + @ifstmt.evaluate(@scope) + end + + it "should not evaluate if statements if test is false" do + Puppet::Parser::Scope.stubs(:true?).returns(false) + + @stmt.expects(:safeevaluate).with(@scope).never + + @ifstmt.evaluate(@scope) + end + + it "should evaluate the else branch if test is false" do + Puppet::Parser::Scope.stubs(:true?).returns(false) + + @else.expects(:safeevaluate).with(@scope) + + @ifelsestmt.evaluate(@scope) + end + + it "should not evaluate the else branch if test is true" do + Puppet::Parser::Scope.stubs(:true?).returns(true) + + @else.expects(:safeevaluate).with(@scope).never + + @ifelsestmt.evaluate(@scope) + end + + it "should reset ephemeral statements after evaluation" do + @scope.expects(:ephemeral_level).returns(:level) + Puppet::Parser::Scope.stubs(:true?).returns(true) + + @stmt.expects(:safeevaluate).with(@scope) + @scope.expects(:unset_ephemeral_var).with(:level) + + @ifstmt.evaluate(@scope) + end + end +end diff --git a/spec/unit/parser/ast/ifstatement_spec_spec.rb b/spec/unit/parser/ast/ifstatement_spec_spec.rb deleted file mode 100755 index 83121cddd..000000000 --- a/spec/unit/parser/ast/ifstatement_spec_spec.rb +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::IfStatement do - before :each do - @scope = Puppet::Parser::Scope.new() - end - - describe "when evaluating" do - - before :each do - @test = stub 'test' - @test.stubs(:safeevaluate).with(@scope) - - @stmt = stub 'stmt' - @stmt.stubs(:safeevaluate).with(@scope) - - @else = stub 'else' - @else.stubs(:safeevaluate).with(@scope) - - @ifstmt = Puppet::Parser::AST::IfStatement.new :test => @test, :statements => @stmt - @ifelsestmt = Puppet::Parser::AST::IfStatement.new :test => @test, :statements => @stmt, :else => @else - end - - it "should evaluate test" do - Puppet::Parser::Scope.stubs(:true?).returns(false) - - @test.expects(:safeevaluate).with(@scope) - - @ifstmt.evaluate(@scope) - end - - it "should evaluate if statements if test is true" do - Puppet::Parser::Scope.stubs(:true?).returns(true) - - @stmt.expects(:safeevaluate).with(@scope) - - @ifstmt.evaluate(@scope) - end - - it "should not evaluate if statements if test is false" do - Puppet::Parser::Scope.stubs(:true?).returns(false) - - @stmt.expects(:safeevaluate).with(@scope).never - - @ifstmt.evaluate(@scope) - end - - it "should evaluate the else branch if test is false" do - Puppet::Parser::Scope.stubs(:true?).returns(false) - - @else.expects(:safeevaluate).with(@scope) - - @ifelsestmt.evaluate(@scope) - end - - it "should not evaluate the else branch if test is true" do - Puppet::Parser::Scope.stubs(:true?).returns(true) - - @else.expects(:safeevaluate).with(@scope).never - - @ifelsestmt.evaluate(@scope) - end - - it "should reset ephemeral statements after evaluation" do - @scope.expects(:ephemeral_level).returns(:level) - Puppet::Parser::Scope.stubs(:true?).returns(true) - - @stmt.expects(:safeevaluate).with(@scope) - @scope.expects(:unset_ephemeral_var).with(:level) - - @ifstmt.evaluate(@scope) - end - end -end diff --git a/spec/unit/parser/ast/in_operator_spec.rb b/spec/unit/parser/ast/in_operator_spec.rb new file mode 100644 index 000000000..9d3cab712 --- /dev/null +++ b/spec/unit/parser/ast/in_operator_spec.rb @@ -0,0 +1,60 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/parser/ast/in_operator' + +describe Puppet::Parser::AST::InOperator do + before :each do + @scope = Puppet::Parser::Scope.new() + + @lval = stub 'lval' + @lval.stubs(:safeevaluate).with(@scope).returns("left") + + @rval = stub 'rval' + @rval.stubs(:safeevaluate).with(@scope).returns("right") + + @operator = Puppet::Parser::AST::InOperator.new :lval => @lval, :rval => @rval + end + + it "should evaluate the left operand" do + @lval.expects(:safeevaluate).with(@scope).returns("string") + + @operator.evaluate(@scope) + end + + it "should evaluate the right operand" do + @rval.expects(:safeevaluate).with(@scope).returns("string") + + @operator.evaluate(@scope) + end + + it "should raise an argument error if lval is not a string" do + @lval.expects(:safeevaluate).with(@scope).returns([12,13]) + + lambda { @operator.evaluate(@scope) }.should raise_error + end + + it "should raise an argument error if rval doesn't support the include? method" do + @rval.expects(:safeevaluate).with(@scope).returns(stub('value')) + + lambda { @operator.evaluate(@scope) }.should raise_error + end + + it "should not raise an argument error if rval supports the include? method" do + @rval.expects(:safeevaluate).with(@scope).returns(stub('value', :include? => true)) + + lambda { @operator.evaluate(@scope) }.should_not raise_error + end + + it "should return rval.include?(lval)" do + lval = stub 'lvalue', :is_a? => true + @lval.stubs(:safeevaluate).with(@scope).returns(lval) + + rval = stub 'rvalue' + @rval.stubs(:safeevaluate).with(@scope).returns(rval) + rval.expects(:include?).with(lval).returns(:result) + + @operator.evaluate(@scope).should == :result + end +end diff --git a/spec/unit/parser/ast/in_operator_spec_spec.rb b/spec/unit/parser/ast/in_operator_spec_spec.rb deleted file mode 100644 index 9d3cab712..000000000 --- a/spec/unit/parser/ast/in_operator_spec_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/parser/ast/in_operator' - -describe Puppet::Parser::AST::InOperator do - before :each do - @scope = Puppet::Parser::Scope.new() - - @lval = stub 'lval' - @lval.stubs(:safeevaluate).with(@scope).returns("left") - - @rval = stub 'rval' - @rval.stubs(:safeevaluate).with(@scope).returns("right") - - @operator = Puppet::Parser::AST::InOperator.new :lval => @lval, :rval => @rval - end - - it "should evaluate the left operand" do - @lval.expects(:safeevaluate).with(@scope).returns("string") - - @operator.evaluate(@scope) - end - - it "should evaluate the right operand" do - @rval.expects(:safeevaluate).with(@scope).returns("string") - - @operator.evaluate(@scope) - end - - it "should raise an argument error if lval is not a string" do - @lval.expects(:safeevaluate).with(@scope).returns([12,13]) - - lambda { @operator.evaluate(@scope) }.should raise_error - end - - it "should raise an argument error if rval doesn't support the include? method" do - @rval.expects(:safeevaluate).with(@scope).returns(stub('value')) - - lambda { @operator.evaluate(@scope) }.should raise_error - end - - it "should not raise an argument error if rval supports the include? method" do - @rval.expects(:safeevaluate).with(@scope).returns(stub('value', :include? => true)) - - lambda { @operator.evaluate(@scope) }.should_not raise_error - end - - it "should return rval.include?(lval)" do - lval = stub 'lvalue', :is_a? => true - @lval.stubs(:safeevaluate).with(@scope).returns(lval) - - rval = stub 'rvalue' - @rval.stubs(:safeevaluate).with(@scope).returns(rval) - rval.expects(:include?).with(lval).returns(:result) - - @operator.evaluate(@scope).should == :result - end -end diff --git a/spec/unit/parser/ast/leaf_spec.rb b/spec/unit/parser/ast/leaf_spec.rb new file mode 100755 index 000000000..d5534debb --- /dev/null +++ b/spec/unit/parser/ast/leaf_spec.rb @@ -0,0 +1,367 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Leaf do + before :each do + @scope = stub 'scope' + @value = stub 'value' + @leaf = Puppet::Parser::AST::Leaf.new(:value => @value) + end + + it "should have a evaluate_match method" do + Puppet::Parser::AST::Leaf.new(:value => "value").should respond_to(:evaluate_match) + end + + describe "when evaluate_match is called" do + it "should evaluate itself" do + @leaf.expects(:safeevaluate).with(@scope) + + @leaf.evaluate_match("value", @scope) + end + + it "should match values by equality" do + @value.stubs(:==).returns(false) + @leaf.stubs(:safeevaluate).with(@scope).returns(@value) + @value.expects(:==).with("value") + + @leaf.evaluate_match("value", @scope) + end + + it "should downcase the evaluated value if wanted" do + @leaf.stubs(:safeevaluate).with(@scope).returns(@value) + @value.expects(:downcase).returns("value") + + @leaf.evaluate_match("value", @scope) + end + + it "should match undef if value is an empty string" do + @leaf.stubs(:safeevaluate).with(@scope).returns("") + + @leaf.evaluate_match(:undef, @scope).should be_true + end + + it "should downcase the parameter value if wanted" do + parameter = stub 'parameter' + parameter.expects(:downcase).returns("value") + + @leaf.evaluate_match(parameter, @scope) + end + end + + describe "when converting to string" do + it "should transform its value to string" do + value = stub 'value', :is_a? => true + value.expects(:to_s) + Puppet::Parser::AST::Leaf.new( :value => value ).to_s + end + end + + it "should have a match method" do + @leaf.should respond_to(:match) + end + + it "should delegate match to ==" do + @value.expects(:==).with("value") + + @leaf.match("value") + end +end + +describe Puppet::Parser::AST::FlatString do + describe "when converting to string" do + it "should transform its value to a quoted string" do + value = stub 'value', :is_a? => true, :to_s => "ab" + Puppet::Parser::AST::FlatString.new( :value => value ).to_s.should == "\"ab\"" + end + end +end + +describe Puppet::Parser::AST::String do + describe "when converting to string" do + it "should transform its value to a quoted string" do + value = stub 'value', :is_a? => true, :to_s => "ab" + Puppet::Parser::AST::String.new( :value => value ).to_s.should == "\"ab\"" + end + end +end + +describe Puppet::Parser::AST::Undef do + before :each do + @scope = stub 'scope' + @undef = Puppet::Parser::AST::Undef.new(:value => :undef) + end + + it "should match undef with undef" do + @undef.evaluate_match(:undef, @scope).should be_true + end + + it "should not match undef with an empty string" do + @undef.evaluate_match("", @scope).should be_false + end +end + +describe Puppet::Parser::AST::HashOrArrayAccess do + before :each do + @scope = stub 'scope' + end + + describe "when evaluating" do + it "should evaluate the variable part if necessary" do + @scope.stubs(:lookupvar).with("a").returns(["b"]) + + variable = stub 'variable', :evaluate => "a" + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => variable, :key => 0 ) + + variable.expects(:safeevaluate).with(@scope).returns("a") + + access.evaluate(@scope).should == "b" + end + + it "should evaluate the access key part if necessary" do + @scope.stubs(:lookupvar).with("a").returns(["b"]) + + index = stub 'index', :evaluate => 0 + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => index ) + + index.expects(:safeevaluate).with(@scope).returns(0) + + access.evaluate(@scope).should == "b" + end + + it "should be able to return an array member" do + @scope.stubs(:lookupvar).with("a").returns(["val1", "val2", "val3"]) + + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => 1 ) + + access.evaluate(@scope).should == "val2" + end + + it "should be able to return an hash value" do + @scope.stubs(:lookupvar).with("a").returns({ "key1" => "val1", "key2" => "val2", "key3" => "val3" }) + + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) + + access.evaluate(@scope).should == "val2" + end + + it "should raise an error if the variable lookup didn't return an hash or an array" do + @scope.stubs(:lookupvar).with("a").returns("I'm a string") + + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) + + lambda { access.evaluate(@scope) }.should raise_error + end + + it "should raise an error if the variable wasn't in the scope" do + @scope.stubs(:lookupvar).with("a").returns(nil) + + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) + + lambda { access.evaluate(@scope) }.should raise_error + end + + it "should return a correct string representation" do + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) + access.to_s.should == '$a[key2]' + end + + it "should work with recursive hash access" do + @scope.stubs(:lookupvar).with("a").returns({ "key" => { "subkey" => "b" }}) + + access1 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") + access2 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => access1, :key => "subkey") + + access2.evaluate(@scope).should == 'b' + end + + it "should work with interleaved array and hash access" do + @scope.stubs(:lookupvar).with("a").returns({ "key" => [ "a" , "b" ]}) + + access1 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") + access2 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => access1, :key => 1) + + access2.evaluate(@scope).should == 'b' + end + end + + describe "when assigning" do + it "should add a new key and value" do + scope = Puppet::Parser::Scope.new + scope.setvar("a", { 'a' => 'b' }) + + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "b") + access.assign(scope, "c" ) + + scope.lookupvar("a").should be_include("b") + end + + it "should raise an error when trying to overwrite an hash value" do + @scope.stubs(:lookupvar).with("a").returns({ "key" => [ "a" , "b" ]}) + access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") + + lambda { access.assign(@scope, "test") }.should raise_error + end + end +end + +describe Puppet::Parser::AST::Regex do + before :each do + @scope = stub 'scope' + end + + describe "when initializing" do + it "should create a Regexp with its content when value is not a Regexp" do + Regexp.expects(:new).with("/ab/") + + Puppet::Parser::AST::Regex.new :value => "/ab/" + end + + it "should not create a Regexp with its content when value is a Regexp" do + value = Regexp.new("/ab/") + Regexp.expects(:new).with("/ab/").never + + Puppet::Parser::AST::Regex.new :value => value + end + end + + describe "when evaluating" do + it "should return self" do + val = Puppet::Parser::AST::Regex.new :value => "/ab/" + + val.evaluate(@scope).should === val + end + end + + describe "when evaluate_match" do + before :each do + @value = stub 'regex' + @value.stubs(:match).with("value").returns(true) + Regexp.stubs(:new).returns(@value) + @regex = Puppet::Parser::AST::Regex.new :value => "/ab/" + end + + it "should issue the regexp match" do + @value.expects(:match).with("value") + + @regex.evaluate_match("value", @scope) + end + + it "should not downcase the paramater value" do + @value.expects(:match).with("VaLuE") + + @regex.evaluate_match("VaLuE", @scope) + end + + it "should set ephemeral scope vars if there is a match" do + @scope.expects(:ephemeral_from).with(true, nil, nil) + + @regex.evaluate_match("value", @scope) + end + + it "should return the match to the caller" do + @value.stubs(:match).with("value").returns(:match) + @scope.stubs(:ephemeral_from) + + @regex.evaluate_match("value", @scope) + end + end + + it "should return the regex source with to_s" do + regex = stub 'regex' + Regexp.stubs(:new).returns(regex) + + val = Puppet::Parser::AST::Regex.new :value => "/ab/" + + regex.expects(:source) + + val.to_s + end + + it "should delegate match to the underlying regexp match method" do + regex = Regexp.new("/ab/") + val = Puppet::Parser::AST::Regex.new :value => regex + + regex.expects(:match).with("value") + + val.match("value") + end +end + +describe Puppet::Parser::AST::Variable do + before :each do + @scope = stub 'scope' + @var = Puppet::Parser::AST::Variable.new(:value => "myvar") + end + + it "should lookup the variable in scope" do + @scope.expects(:lookupvar).with("myvar", false).returns(:myvalue) + @var.safeevaluate(@scope).should == :myvalue + end + + it "should return undef if the variable wasn't set" do + @scope.expects(:lookupvar).with("myvar", false).returns(:undefined) + @var.safeevaluate(@scope).should == :undef + end + + describe "when converting to string" do + it "should transform its value to a variable" do + value = stub 'value', :is_a? => true, :to_s => "myvar" + Puppet::Parser::AST::Variable.new( :value => value ).to_s.should == "\$myvar" + end + end +end + +describe Puppet::Parser::AST::HostName do + before :each do + @scope = stub 'scope' + @value = stub 'value', :=~ => false + @value.stubs(:to_s).returns(@value) + @value.stubs(:downcase).returns(@value) + @host = Puppet::Parser::AST::HostName.new( :value => @value) + end + + it "should raise an error if hostname is not valid" do + lambda { Puppet::Parser::AST::HostName.new( :value => "not an hostname!" ) }.should raise_error + end + + it "should not raise an error if hostname is a regex" do + lambda { Puppet::Parser::AST::HostName.new( :value => Puppet::Parser::AST::Regex.new(:value => "/test/") ) }.should_not raise_error + end + + it "should stringify the value" do + value = stub 'value', :=~ => false + + value.expects(:to_s).returns("test") + + Puppet::Parser::AST::HostName.new(:value => value) + end + + it "should downcase the value" do + value = stub 'value', :=~ => false + value.stubs(:to_s).returns("UPCASED") + host = Puppet::Parser::AST::HostName.new(:value => value) + + host.value == "upcased" + end + + it "should evaluate to its value" do + @host.evaluate(@scope).should == @value + end + + it "should delegate eql? to the underlying value if it is an HostName" do + @value.expects(:eql?).with("value") + @host.eql?("value") + end + + it "should delegate eql? to the underlying value if it is not an HostName" do + value = stub 'compared', :is_a? => true, :value => "value" + @value.expects(:eql?).with("value") + @host.eql?(value) + end + + it "should delegate hash to the underlying value" do + @value.expects(:hash) + @host.hash + end +end diff --git a/spec/unit/parser/ast/leaf_spec_spec.rb b/spec/unit/parser/ast/leaf_spec_spec.rb deleted file mode 100755 index d5534debb..000000000 --- a/spec/unit/parser/ast/leaf_spec_spec.rb +++ /dev/null @@ -1,367 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::Leaf do - before :each do - @scope = stub 'scope' - @value = stub 'value' - @leaf = Puppet::Parser::AST::Leaf.new(:value => @value) - end - - it "should have a evaluate_match method" do - Puppet::Parser::AST::Leaf.new(:value => "value").should respond_to(:evaluate_match) - end - - describe "when evaluate_match is called" do - it "should evaluate itself" do - @leaf.expects(:safeevaluate).with(@scope) - - @leaf.evaluate_match("value", @scope) - end - - it "should match values by equality" do - @value.stubs(:==).returns(false) - @leaf.stubs(:safeevaluate).with(@scope).returns(@value) - @value.expects(:==).with("value") - - @leaf.evaluate_match("value", @scope) - end - - it "should downcase the evaluated value if wanted" do - @leaf.stubs(:safeevaluate).with(@scope).returns(@value) - @value.expects(:downcase).returns("value") - - @leaf.evaluate_match("value", @scope) - end - - it "should match undef if value is an empty string" do - @leaf.stubs(:safeevaluate).with(@scope).returns("") - - @leaf.evaluate_match(:undef, @scope).should be_true - end - - it "should downcase the parameter value if wanted" do - parameter = stub 'parameter' - parameter.expects(:downcase).returns("value") - - @leaf.evaluate_match(parameter, @scope) - end - end - - describe "when converting to string" do - it "should transform its value to string" do - value = stub 'value', :is_a? => true - value.expects(:to_s) - Puppet::Parser::AST::Leaf.new( :value => value ).to_s - end - end - - it "should have a match method" do - @leaf.should respond_to(:match) - end - - it "should delegate match to ==" do - @value.expects(:==).with("value") - - @leaf.match("value") - end -end - -describe Puppet::Parser::AST::FlatString do - describe "when converting to string" do - it "should transform its value to a quoted string" do - value = stub 'value', :is_a? => true, :to_s => "ab" - Puppet::Parser::AST::FlatString.new( :value => value ).to_s.should == "\"ab\"" - end - end -end - -describe Puppet::Parser::AST::String do - describe "when converting to string" do - it "should transform its value to a quoted string" do - value = stub 'value', :is_a? => true, :to_s => "ab" - Puppet::Parser::AST::String.new( :value => value ).to_s.should == "\"ab\"" - end - end -end - -describe Puppet::Parser::AST::Undef do - before :each do - @scope = stub 'scope' - @undef = Puppet::Parser::AST::Undef.new(:value => :undef) - end - - it "should match undef with undef" do - @undef.evaluate_match(:undef, @scope).should be_true - end - - it "should not match undef with an empty string" do - @undef.evaluate_match("", @scope).should be_false - end -end - -describe Puppet::Parser::AST::HashOrArrayAccess do - before :each do - @scope = stub 'scope' - end - - describe "when evaluating" do - it "should evaluate the variable part if necessary" do - @scope.stubs(:lookupvar).with("a").returns(["b"]) - - variable = stub 'variable', :evaluate => "a" - access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => variable, :key => 0 ) - - variable.expects(:safeevaluate).with(@scope).returns("a") - - access.evaluate(@scope).should == "b" - end - - it "should evaluate the access key part if necessary" do - @scope.stubs(:lookupvar).with("a").returns(["b"]) - - index = stub 'index', :evaluate => 0 - access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => index ) - - index.expects(:safeevaluate).with(@scope).returns(0) - - access.evaluate(@scope).should == "b" - end - - it "should be able to return an array member" do - @scope.stubs(:lookupvar).with("a").returns(["val1", "val2", "val3"]) - - access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => 1 ) - - access.evaluate(@scope).should == "val2" - end - - it "should be able to return an hash value" do - @scope.stubs(:lookupvar).with("a").returns({ "key1" => "val1", "key2" => "val2", "key3" => "val3" }) - - access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) - - access.evaluate(@scope).should == "val2" - end - - it "should raise an error if the variable lookup didn't return an hash or an array" do - @scope.stubs(:lookupvar).with("a").returns("I'm a string") - - access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) - - lambda { access.evaluate(@scope) }.should raise_error - end - - it "should raise an error if the variable wasn't in the scope" do - @scope.stubs(:lookupvar).with("a").returns(nil) - - access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) - - lambda { access.evaluate(@scope) }.should raise_error - end - - it "should return a correct string representation" do - access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) - access.to_s.should == '$a[key2]' - end - - it "should work with recursive hash access" do - @scope.stubs(:lookupvar).with("a").returns({ "key" => { "subkey" => "b" }}) - - access1 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") - access2 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => access1, :key => "subkey") - - access2.evaluate(@scope).should == 'b' - end - - it "should work with interleaved array and hash access" do - @scope.stubs(:lookupvar).with("a").returns({ "key" => [ "a" , "b" ]}) - - access1 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") - access2 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => access1, :key => 1) - - access2.evaluate(@scope).should == 'b' - end - end - - describe "when assigning" do - it "should add a new key and value" do - scope = Puppet::Parser::Scope.new - scope.setvar("a", { 'a' => 'b' }) - - access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "b") - access.assign(scope, "c" ) - - scope.lookupvar("a").should be_include("b") - end - - it "should raise an error when trying to overwrite an hash value" do - @scope.stubs(:lookupvar).with("a").returns({ "key" => [ "a" , "b" ]}) - access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") - - lambda { access.assign(@scope, "test") }.should raise_error - end - end -end - -describe Puppet::Parser::AST::Regex do - before :each do - @scope = stub 'scope' - end - - describe "when initializing" do - it "should create a Regexp with its content when value is not a Regexp" do - Regexp.expects(:new).with("/ab/") - - Puppet::Parser::AST::Regex.new :value => "/ab/" - end - - it "should not create a Regexp with its content when value is a Regexp" do - value = Regexp.new("/ab/") - Regexp.expects(:new).with("/ab/").never - - Puppet::Parser::AST::Regex.new :value => value - end - end - - describe "when evaluating" do - it "should return self" do - val = Puppet::Parser::AST::Regex.new :value => "/ab/" - - val.evaluate(@scope).should === val - end - end - - describe "when evaluate_match" do - before :each do - @value = stub 'regex' - @value.stubs(:match).with("value").returns(true) - Regexp.stubs(:new).returns(@value) - @regex = Puppet::Parser::AST::Regex.new :value => "/ab/" - end - - it "should issue the regexp match" do - @value.expects(:match).with("value") - - @regex.evaluate_match("value", @scope) - end - - it "should not downcase the paramater value" do - @value.expects(:match).with("VaLuE") - - @regex.evaluate_match("VaLuE", @scope) - end - - it "should set ephemeral scope vars if there is a match" do - @scope.expects(:ephemeral_from).with(true, nil, nil) - - @regex.evaluate_match("value", @scope) - end - - it "should return the match to the caller" do - @value.stubs(:match).with("value").returns(:match) - @scope.stubs(:ephemeral_from) - - @regex.evaluate_match("value", @scope) - end - end - - it "should return the regex source with to_s" do - regex = stub 'regex' - Regexp.stubs(:new).returns(regex) - - val = Puppet::Parser::AST::Regex.new :value => "/ab/" - - regex.expects(:source) - - val.to_s - end - - it "should delegate match to the underlying regexp match method" do - regex = Regexp.new("/ab/") - val = Puppet::Parser::AST::Regex.new :value => regex - - regex.expects(:match).with("value") - - val.match("value") - end -end - -describe Puppet::Parser::AST::Variable do - before :each do - @scope = stub 'scope' - @var = Puppet::Parser::AST::Variable.new(:value => "myvar") - end - - it "should lookup the variable in scope" do - @scope.expects(:lookupvar).with("myvar", false).returns(:myvalue) - @var.safeevaluate(@scope).should == :myvalue - end - - it "should return undef if the variable wasn't set" do - @scope.expects(:lookupvar).with("myvar", false).returns(:undefined) - @var.safeevaluate(@scope).should == :undef - end - - describe "when converting to string" do - it "should transform its value to a variable" do - value = stub 'value', :is_a? => true, :to_s => "myvar" - Puppet::Parser::AST::Variable.new( :value => value ).to_s.should == "\$myvar" - end - end -end - -describe Puppet::Parser::AST::HostName do - before :each do - @scope = stub 'scope' - @value = stub 'value', :=~ => false - @value.stubs(:to_s).returns(@value) - @value.stubs(:downcase).returns(@value) - @host = Puppet::Parser::AST::HostName.new( :value => @value) - end - - it "should raise an error if hostname is not valid" do - lambda { Puppet::Parser::AST::HostName.new( :value => "not an hostname!" ) }.should raise_error - end - - it "should not raise an error if hostname is a regex" do - lambda { Puppet::Parser::AST::HostName.new( :value => Puppet::Parser::AST::Regex.new(:value => "/test/") ) }.should_not raise_error - end - - it "should stringify the value" do - value = stub 'value', :=~ => false - - value.expects(:to_s).returns("test") - - Puppet::Parser::AST::HostName.new(:value => value) - end - - it "should downcase the value" do - value = stub 'value', :=~ => false - value.stubs(:to_s).returns("UPCASED") - host = Puppet::Parser::AST::HostName.new(:value => value) - - host.value == "upcased" - end - - it "should evaluate to its value" do - @host.evaluate(@scope).should == @value - end - - it "should delegate eql? to the underlying value if it is an HostName" do - @value.expects(:eql?).with("value") - @host.eql?("value") - end - - it "should delegate eql? to the underlying value if it is not an HostName" do - value = stub 'compared', :is_a? => true, :value => "value" - @value.expects(:eql?).with("value") - @host.eql?(value) - end - - it "should delegate hash to the underlying value" do - @value.expects(:hash) - @host.hash - end -end diff --git a/spec/unit/parser/ast/match_operator_spec.rb b/spec/unit/parser/ast/match_operator_spec.rb new file mode 100755 index 000000000..985cf60d5 --- /dev/null +++ b/spec/unit/parser/ast/match_operator_spec.rb @@ -0,0 +1,50 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::MatchOperator do + before :each do + @scope = Puppet::Parser::Scope.new() + + @lval = stub 'lval' + @lval.stubs(:safeevaluate).with(@scope).returns("this is a string") + + @rval = stub 'rval' + @rval.stubs(:evaluate_match) + + @operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => "=~" + end + + it "should evaluate the left operand" do + @lval.expects(:safeevaluate).with(@scope) + + @operator.evaluate(@scope) + end + + it "should fail for an unknown operator" do + lambda { operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :operator => "unknown", :rval => @rval }.should raise_error + end + + it "should evaluate_match the left operand" do + @rval.expects(:evaluate_match).with("this is a string", @scope).returns(:match) + + @operator.evaluate(@scope) + end + + { "=~" => true, "!~" => false }.each do |op, res| + it "should return #{res} if the regexp matches with #{op}" do + match = stub 'match' + @rval.stubs(:evaluate_match).with("this is a string", @scope).returns(match) + + operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => op + operator.evaluate(@scope).should == res + end + + it "should return #{!res} if the regexp doesn't match" do + @rval.stubs(:evaluate_match).with("this is a string", @scope).returns(nil) + + operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => op + operator.evaluate(@scope).should == !res + end + end +end diff --git a/spec/unit/parser/ast/match_operator_spec_spec.rb b/spec/unit/parser/ast/match_operator_spec_spec.rb deleted file mode 100755 index 985cf60d5..000000000 --- a/spec/unit/parser/ast/match_operator_spec_spec.rb +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::MatchOperator do - before :each do - @scope = Puppet::Parser::Scope.new() - - @lval = stub 'lval' - @lval.stubs(:safeevaluate).with(@scope).returns("this is a string") - - @rval = stub 'rval' - @rval.stubs(:evaluate_match) - - @operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => "=~" - end - - it "should evaluate the left operand" do - @lval.expects(:safeevaluate).with(@scope) - - @operator.evaluate(@scope) - end - - it "should fail for an unknown operator" do - lambda { operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :operator => "unknown", :rval => @rval }.should raise_error - end - - it "should evaluate_match the left operand" do - @rval.expects(:evaluate_match).with("this is a string", @scope).returns(:match) - - @operator.evaluate(@scope) - end - - { "=~" => true, "!~" => false }.each do |op, res| - it "should return #{res} if the regexp matches with #{op}" do - match = stub 'match' - @rval.stubs(:evaluate_match).with("this is a string", @scope).returns(match) - - operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => op - operator.evaluate(@scope).should == res - end - - it "should return #{!res} if the regexp doesn't match" do - @rval.stubs(:evaluate_match).with("this is a string", @scope).returns(nil) - - operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => op - operator.evaluate(@scope).should == !res - end - end -end diff --git a/spec/unit/parser/ast/minus_spec.rb b/spec/unit/parser/ast/minus_spec.rb new file mode 100755 index 000000000..782fd3e0c --- /dev/null +++ b/spec/unit/parser/ast/minus_spec.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Minus do + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should evaluate its argument" do + value = stub "value" + value.expects(:safeevaluate).with(@scope).returns(123) + + operator = Puppet::Parser::AST::Minus.new :value => value + operator.evaluate(@scope) + end + + it "should fail if argument is not a string or integer" do + array_ast = stub 'array_ast', :safeevaluate => [2] + operator = Puppet::Parser::AST::Minus.new :value => array_ast + lambda { operator.evaluate(@scope) }.should raise_error + end + + it "should work with integer as string" do + string = stub 'string', :safeevaluate => "123" + operator = Puppet::Parser::AST::Minus.new :value => string + operator.evaluate(@scope).should == -123 + end + + it "should work with integers" do + int = stub 'int', :safeevaluate => 123 + operator = Puppet::Parser::AST::Minus.new :value => int + operator.evaluate(@scope).should == -123 + end + +end diff --git a/spec/unit/parser/ast/minus_spec_spec.rb b/spec/unit/parser/ast/minus_spec_spec.rb deleted file mode 100755 index 782fd3e0c..000000000 --- a/spec/unit/parser/ast/minus_spec_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::Minus do - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should evaluate its argument" do - value = stub "value" - value.expects(:safeevaluate).with(@scope).returns(123) - - operator = Puppet::Parser::AST::Minus.new :value => value - operator.evaluate(@scope) - end - - it "should fail if argument is not a string or integer" do - array_ast = stub 'array_ast', :safeevaluate => [2] - operator = Puppet::Parser::AST::Minus.new :value => array_ast - lambda { operator.evaluate(@scope) }.should raise_error - end - - it "should work with integer as string" do - string = stub 'string', :safeevaluate => "123" - operator = Puppet::Parser::AST::Minus.new :value => string - operator.evaluate(@scope).should == -123 - end - - it "should work with integers" do - int = stub 'int', :safeevaluate => 123 - operator = Puppet::Parser::AST::Minus.new :value => int - operator.evaluate(@scope).should == -123 - end - -end diff --git a/spec/unit/parser/ast/nop_spec.rb b/spec/unit/parser/ast/nop_spec.rb new file mode 100755 index 000000000..5a7132586 --- /dev/null +++ b/spec/unit/parser/ast/nop_spec.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Nop do + + before do + @scope = mock 'scope' + end + + it "should do nothing on evaluation" do + Puppet::Parser::AST.expects(:safeevaluate).never + Puppet::Parser::AST::Nop.new({}).evaluate(@scope) + end + + it "should not return anything" do + Puppet::Parser::AST::Nop.new({}).evaluate(@scope).should be_nil + end + +end diff --git a/spec/unit/parser/ast/nop_spec_spec.rb b/spec/unit/parser/ast/nop_spec_spec.rb deleted file mode 100755 index 5a7132586..000000000 --- a/spec/unit/parser/ast/nop_spec_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::Nop do - - before do - @scope = mock 'scope' - end - - it "should do nothing on evaluation" do - Puppet::Parser::AST.expects(:safeevaluate).never - Puppet::Parser::AST::Nop.new({}).evaluate(@scope) - end - - it "should not return anything" do - Puppet::Parser::AST::Nop.new({}).evaluate(@scope).should be_nil - end - -end diff --git a/spec/unit/parser/ast/not_spec.rb b/spec/unit/parser/ast/not_spec.rb new file mode 100755 index 000000000..a3bf0b85d --- /dev/null +++ b/spec/unit/parser/ast/not_spec.rb @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Not do + before :each do + @scope = Puppet::Parser::Scope.new() + @true_ast = Puppet::Parser::AST::Boolean.new( :value => true) + @false_ast = Puppet::Parser::AST::Boolean.new( :value => false) + end + + it "should evaluate its child expression" do + val = stub "val" + val.expects(:safeevaluate).with(@scope) + + operator = Puppet::Parser::AST::Not.new :value => val + operator.evaluate(@scope) + end + + it "should return true for ! false" do + operator = Puppet::Parser::AST::Not.new :value => @false_ast + operator.evaluate(@scope).should == true + end + + it "should return false for ! true" do + operator = Puppet::Parser::AST::Not.new :value => @true_ast + operator.evaluate(@scope).should == false + end + +end diff --git a/spec/unit/parser/ast/not_spec_spec.rb b/spec/unit/parser/ast/not_spec_spec.rb deleted file mode 100755 index a3bf0b85d..000000000 --- a/spec/unit/parser/ast/not_spec_spec.rb +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::Not do - before :each do - @scope = Puppet::Parser::Scope.new() - @true_ast = Puppet::Parser::AST::Boolean.new( :value => true) - @false_ast = Puppet::Parser::AST::Boolean.new( :value => false) - end - - it "should evaluate its child expression" do - val = stub "val" - val.expects(:safeevaluate).with(@scope) - - operator = Puppet::Parser::AST::Not.new :value => val - operator.evaluate(@scope) - end - - it "should return true for ! false" do - operator = Puppet::Parser::AST::Not.new :value => @false_ast - operator.evaluate(@scope).should == true - end - - it "should return false for ! true" do - operator = Puppet::Parser::AST::Not.new :value => @true_ast - operator.evaluate(@scope).should == false - end - -end diff --git a/spec/unit/parser/ast/relationship_spec.rb b/spec/unit/parser/ast/relationship_spec.rb new file mode 100644 index 000000000..acb46e4ce --- /dev/null +++ b/spec/unit/parser/ast/relationship_spec.rb @@ -0,0 +1,88 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Relationship do + before do + @class = Puppet::Parser::AST::Relationship + end + + it "should set its 'left' and 'right' arguments accordingly" do + dep = @class.new(:left, :right, '->') + dep.left.should == :left + dep.right.should == :right + end + + it "should set its arrow to whatever arrow is passed" do + @class.new(:left, :right, '->').arrow.should == '->' + end + + it "should set its type to :relationship if the relationship type is '<-'" do + @class.new(:left, :right, '<-').type.should == :relationship + end + + it "should set its type to :relationship if the relationship type is '->'" do + @class.new(:left, :right, '->').type.should == :relationship + end + + it "should set its type to :subscription if the relationship type is '~>'" do + @class.new(:left, :right, '~>').type.should == :subscription + end + + it "should set its type to :subscription if the relationship type is '<~'" do + @class.new(:left, :right, '<~').type.should == :subscription + end + + it "should set its line and file if provided" do + dep = @class.new(:left, :right, '->', :line => 50, :file => "/foo") + dep.line.should == 50 + dep.file.should == "/foo" + end + + describe "when evaluating" do + before do + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) + @scope = Puppet::Parser::Scope.new(:compiler => @compiler) + end + + it "should create a relationship with the evaluated source and target and add it to the scope" do + source = stub 'source', :safeevaluate => :left + target = stub 'target', :safeevaluate => :right + @class.new(source, target, '->').evaluate(@scope) + @compiler.relationships[0].source.should == :left + @compiler.relationships[0].target.should == :right + end + + describe "a chained relationship" do + before do + @left = stub 'left', :safeevaluate => :left + @middle = stub 'middle', :safeevaluate => :middle + @right = stub 'right', :safeevaluate => :right + @first = @class.new(@left, @middle, '->') + @second = @class.new(@first, @right, '->') + end + + it "should evaluate the relationship to the left" do + @first.expects(:evaluate).with(@scope).returns Puppet::Parser::Relationship.new(:left, :right, :relationship) + + @second.evaluate(@scope) + end + + it "should use the right side of the left relationship as its source" do + @second.evaluate(@scope) + + @compiler.relationships[0].source.should == :left + @compiler.relationships[0].target.should == :middle + @compiler.relationships[1].source.should == :middle + @compiler.relationships[1].target.should == :right + end + + it "should only evaluate a given AST node once" do + @left.expects(:safeevaluate).once.returns :left + @middle.expects(:safeevaluate).once.returns :middle + @right.expects(:safeevaluate).once.returns :right + @second.evaluate(@scope) + end + end + end +end diff --git a/spec/unit/parser/ast/relationship_spec_spec.rb b/spec/unit/parser/ast/relationship_spec_spec.rb deleted file mode 100644 index acb46e4ce..000000000 --- a/spec/unit/parser/ast/relationship_spec_spec.rb +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::Relationship do - before do - @class = Puppet::Parser::AST::Relationship - end - - it "should set its 'left' and 'right' arguments accordingly" do - dep = @class.new(:left, :right, '->') - dep.left.should == :left - dep.right.should == :right - end - - it "should set its arrow to whatever arrow is passed" do - @class.new(:left, :right, '->').arrow.should == '->' - end - - it "should set its type to :relationship if the relationship type is '<-'" do - @class.new(:left, :right, '<-').type.should == :relationship - end - - it "should set its type to :relationship if the relationship type is '->'" do - @class.new(:left, :right, '->').type.should == :relationship - end - - it "should set its type to :subscription if the relationship type is '~>'" do - @class.new(:left, :right, '~>').type.should == :subscription - end - - it "should set its type to :subscription if the relationship type is '<~'" do - @class.new(:left, :right, '<~').type.should == :subscription - end - - it "should set its line and file if provided" do - dep = @class.new(:left, :right, '->', :line => 50, :file => "/foo") - dep.line.should == 50 - dep.file.should == "/foo" - end - - describe "when evaluating" do - before do - @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) - @scope = Puppet::Parser::Scope.new(:compiler => @compiler) - end - - it "should create a relationship with the evaluated source and target and add it to the scope" do - source = stub 'source', :safeevaluate => :left - target = stub 'target', :safeevaluate => :right - @class.new(source, target, '->').evaluate(@scope) - @compiler.relationships[0].source.should == :left - @compiler.relationships[0].target.should == :right - end - - describe "a chained relationship" do - before do - @left = stub 'left', :safeevaluate => :left - @middle = stub 'middle', :safeevaluate => :middle - @right = stub 'right', :safeevaluate => :right - @first = @class.new(@left, @middle, '->') - @second = @class.new(@first, @right, '->') - end - - it "should evaluate the relationship to the left" do - @first.expects(:evaluate).with(@scope).returns Puppet::Parser::Relationship.new(:left, :right, :relationship) - - @second.evaluate(@scope) - end - - it "should use the right side of the left relationship as its source" do - @second.evaluate(@scope) - - @compiler.relationships[0].source.should == :left - @compiler.relationships[0].target.should == :middle - @compiler.relationships[1].source.should == :middle - @compiler.relationships[1].target.should == :right - end - - it "should only evaluate a given AST node once" do - @left.expects(:safeevaluate).once.returns :left - @middle.expects(:safeevaluate).once.returns :middle - @right.expects(:safeevaluate).once.returns :right - @second.evaluate(@scope) - end - end - end -end diff --git a/spec/unit/parser/ast/resource_defaults_spec.rb b/spec/unit/parser/ast/resource_defaults_spec.rb new file mode 100755 index 000000000..b2cec31c6 --- /dev/null +++ b/spec/unit/parser/ast/resource_defaults_spec.rb @@ -0,0 +1,22 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ResourceDefaults do + + ast = Puppet::Parser::AST + + before :each do + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) + @scope = Puppet::Parser::Scope.new(:compiler => @compiler) + @params = Puppet::Parser::AST::ASTArray.new({}) + @compiler.stubs(:add_override) + end + + it "should add defaults when evaluated" do + default = Puppet::Parser::AST::ResourceDefaults.new :type => "file", :parameters => Puppet::Parser::AST::ASTArray.new(:children => []) + default.evaluate @scope + + @scope.lookupdefaults("file").should_not be_nil + end +end diff --git a/spec/unit/parser/ast/resource_defaults_spec_spec.rb b/spec/unit/parser/ast/resource_defaults_spec_spec.rb deleted file mode 100755 index b2cec31c6..000000000 --- a/spec/unit/parser/ast/resource_defaults_spec_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::ResourceDefaults do - - ast = Puppet::Parser::AST - - before :each do - @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) - @scope = Puppet::Parser::Scope.new(:compiler => @compiler) - @params = Puppet::Parser::AST::ASTArray.new({}) - @compiler.stubs(:add_override) - end - - it "should add defaults when evaluated" do - default = Puppet::Parser::AST::ResourceDefaults.new :type => "file", :parameters => Puppet::Parser::AST::ASTArray.new(:children => []) - default.evaluate @scope - - @scope.lookupdefaults("file").should_not be_nil - end -end diff --git a/spec/unit/parser/ast/resource_override_spec.rb b/spec/unit/parser/ast/resource_override_spec.rb new file mode 100755 index 000000000..d327b57cd --- /dev/null +++ b/spec/unit/parser/ast/resource_override_spec.rb @@ -0,0 +1,51 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ResourceOverride do + + ast = Puppet::Parser::AST + + before :each do + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) + @scope = Puppet::Parser::Scope.new(:compiler => @compiler) + @params = ast::ASTArray.new({}) + @compiler.stubs(:add_override) + end + + it "should evaluate the overriden object" do + klass = stub 'klass', :title => "title", :type => "type" + object = mock 'object' + object.expects(:safeevaluate).with(@scope).returns(klass) + ast::ResourceOverride.new(:object => object, :parameters => @params ).evaluate(@scope) + end + + it "should tell the compiler to override the resource with our own" do + @compiler.expects(:add_override) + + klass = stub 'klass', :title => "title", :type => "one" + object = mock 'object', :safeevaluate => klass + ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) + end + + it "should return the overriden resource directly when called with one item" do + klass = stub 'klass', :title => "title", :type => "one" + object = mock 'object', :safeevaluate => klass + override = ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) + override.should be_an_instance_of(Puppet::Parser::Resource) + override.title.should == "title" + override.type.should == "One" + end + + it "should return an array of overriden resources when called with an array of titles" do + klass1 = stub 'klass1', :title => "title1", :type => "one" + klass2 = stub 'klass2', :title => "title2", :type => "one" + + object = mock 'object', :safeevaluate => [klass1,klass2] + + override = ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) + override.should have(2).elements + override.each {|o| o.should be_an_instance_of(Puppet::Parser::Resource) } + end + +end diff --git a/spec/unit/parser/ast/resource_override_spec_spec.rb b/spec/unit/parser/ast/resource_override_spec_spec.rb deleted file mode 100755 index d327b57cd..000000000 --- a/spec/unit/parser/ast/resource_override_spec_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::ResourceOverride do - - ast = Puppet::Parser::AST - - before :each do - @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) - @scope = Puppet::Parser::Scope.new(:compiler => @compiler) - @params = ast::ASTArray.new({}) - @compiler.stubs(:add_override) - end - - it "should evaluate the overriden object" do - klass = stub 'klass', :title => "title", :type => "type" - object = mock 'object' - object.expects(:safeevaluate).with(@scope).returns(klass) - ast::ResourceOverride.new(:object => object, :parameters => @params ).evaluate(@scope) - end - - it "should tell the compiler to override the resource with our own" do - @compiler.expects(:add_override) - - klass = stub 'klass', :title => "title", :type => "one" - object = mock 'object', :safeevaluate => klass - ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) - end - - it "should return the overriden resource directly when called with one item" do - klass = stub 'klass', :title => "title", :type => "one" - object = mock 'object', :safeevaluate => klass - override = ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) - override.should be_an_instance_of(Puppet::Parser::Resource) - override.title.should == "title" - override.type.should == "One" - end - - it "should return an array of overriden resources when called with an array of titles" do - klass1 = stub 'klass1', :title => "title1", :type => "one" - klass2 = stub 'klass2', :title => "title2", :type => "one" - - object = mock 'object', :safeevaluate => [klass1,klass2] - - override = ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) - override.should have(2).elements - override.each {|o| o.should be_an_instance_of(Puppet::Parser::Resource) } - end - -end diff --git a/spec/unit/parser/ast/resource_reference_spec.rb b/spec/unit/parser/ast/resource_reference_spec.rb new file mode 100755 index 000000000..ee42694b9 --- /dev/null +++ b/spec/unit/parser/ast/resource_reference_spec.rb @@ -0,0 +1,46 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ResourceReference do + + ast = Puppet::Parser::AST + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + def newref(type, title) + title = stub 'title', :safeevaluate => title + ref = Puppet::Parser::AST::ResourceReference.new(:type => type, :title => title) + end + + it "should correctly produce reference strings" do + newref("File", "/tmp/yay").evaluate(@scope).to_s.should == "File[/tmp/yay]" + end + + it "should produce a single resource when the title evaluates to a string" do + newref("File", "/tmp/yay").evaluate(@scope).should == Puppet::Resource.new("file", "/tmp/yay") + end + + it "should return an array of resources if given an array of titles" do + titles = mock 'titles', :safeevaluate => ["title1","title2"] + ref = ast::ResourceReference.new( :title => titles, :type => "File" ) + ref.evaluate(@scope).should == [ + Puppet::Resource.new("file", "title1"), + Puppet::Resource.new("file", "title2") + ] + end + + it "should pass its scope's namespaces to all created resource references" do + @scope.add_namespace "foo" + newref("File", "/tmp/yay").evaluate(@scope).namespaces.should == ["foo"] + end + + it "should return a correct representation when converting to string" do + type = stub 'type', :is_a? => true, :to_s => "file" + title = stub 'title', :is_a? => true, :to_s => "[/tmp/a, /tmp/b]" + + ast::ResourceReference.new( :type => type, :title => title ).to_s.should == "File[/tmp/a, /tmp/b]" + end +end diff --git a/spec/unit/parser/ast/resource_reference_spec_spec.rb b/spec/unit/parser/ast/resource_reference_spec_spec.rb deleted file mode 100755 index ee42694b9..000000000 --- a/spec/unit/parser/ast/resource_reference_spec_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::ResourceReference do - - ast = Puppet::Parser::AST - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - def newref(type, title) - title = stub 'title', :safeevaluate => title - ref = Puppet::Parser::AST::ResourceReference.new(:type => type, :title => title) - end - - it "should correctly produce reference strings" do - newref("File", "/tmp/yay").evaluate(@scope).to_s.should == "File[/tmp/yay]" - end - - it "should produce a single resource when the title evaluates to a string" do - newref("File", "/tmp/yay").evaluate(@scope).should == Puppet::Resource.new("file", "/tmp/yay") - end - - it "should return an array of resources if given an array of titles" do - titles = mock 'titles', :safeevaluate => ["title1","title2"] - ref = ast::ResourceReference.new( :title => titles, :type => "File" ) - ref.evaluate(@scope).should == [ - Puppet::Resource.new("file", "title1"), - Puppet::Resource.new("file", "title2") - ] - end - - it "should pass its scope's namespaces to all created resource references" do - @scope.add_namespace "foo" - newref("File", "/tmp/yay").evaluate(@scope).namespaces.should == ["foo"] - end - - it "should return a correct representation when converting to string" do - type = stub 'type', :is_a? => true, :to_s => "file" - title = stub 'title', :is_a? => true, :to_s => "[/tmp/a, /tmp/b]" - - ast::ResourceReference.new( :type => type, :title => title ).to_s.should == "File[/tmp/a, /tmp/b]" - end -end diff --git a/spec/unit/parser/ast/resource_spec.rb b/spec/unit/parser/ast/resource_spec.rb new file mode 100755 index 000000000..2473fda25 --- /dev/null +++ b/spec/unit/parser/ast/resource_spec.rb @@ -0,0 +1,120 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Resource do + ast = Puppet::Parser::AST + + before :each do + @title = Puppet::Parser::AST::String.new(:value => "mytitle") + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) + @scope = Puppet::Parser::Scope.new(:compiler => @compiler) + @scope.stubs(:resource).returns(stub_everything) + @resource = ast::Resource.new(:title => @title, :type => "file", :parameters => ast::ASTArray.new(:children => []) ) + @resource.stubs(:qualified_type).returns("Resource") + end + + it "should evaluate all its parameters" do + param = stub 'param' + param.expects(:safeevaluate).with(@scope).returns Puppet::Parser::Resource::Param.new(:name => "myparam", :value => "myvalue", :source => stub("source")) + @resource.stubs(:parameters).returns [param] + + @resource.evaluate(@scope) + end + + it "should evaluate its title" do + @resource.evaluate(@scope)[0].title.should == "mytitle" + end + + it "should flatten the titles array" do + titles = [] + %w{one two}.each do |title| + titles << Puppet::Parser::AST::String.new(:value => title) + end + + array = Puppet::Parser::AST::ASTArray.new(:children => titles) + + @resource.title = array + result = @resource.evaluate(@scope).collect { |r| r.title } + result.should be_include("one") + result.should be_include("two") + end + + it "should create and return one resource objects per title" do + titles = [] + %w{one two}.each do |title| + titles << Puppet::Parser::AST::String.new(:value => title) + end + + array = Puppet::Parser::AST::ASTArray.new(:children => titles) + + @resource.title = array + result = @resource.evaluate(@scope).collect { |r| r.title } + result.should be_include("one") + result.should be_include("two") + end + + it "should handover resources to the compiler" do + titles = [] + %w{one two}.each do |title| + titles << Puppet::Parser::AST::String.new(:value => title) + end + + array = Puppet::Parser::AST::ASTArray.new(:children => titles) + + @resource.title = array + result = @resource.evaluate(@scope) + + result.each do |res| + @compiler.catalog.resource(res.ref).should be_instance_of(Puppet::Parser::Resource) + end + end + it "should generate virtual resources if it is virtual" do + @resource.virtual = true + + result = @resource.evaluate(@scope) + result[0].should be_virtual + end + + it "should generate virtual and exported resources if it is exported" do + @resource.exported = true + + result = @resource.evaluate(@scope) + result[0].should be_virtual + result[0].should be_exported + end + + # Related to #806, make sure resources always look up the full path to the resource. + describe "when generating qualified resources" do + before do + @scope = Puppet::Parser::Scope.new :compiler => Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) + @parser = Puppet::Parser::Parser.new(Puppet::Node::Environment.new) + @parser.newdefine "one" + @parser.newdefine "one::two" + @parser.newdefine "three" + @twoscope = @scope.newscope(:namespace => "one") + @twoscope.resource = @scope.resource + end + + def resource(type, params = nil) + params ||= Puppet::Parser::AST::ASTArray.new(:children => []) + Puppet::Parser::AST::Resource.new(:type => type, :title => Puppet::Parser::AST::String.new(:value => "myresource"), :parameters => params) + end + + it "should be able to generate resources with fully qualified type information" do + resource("two").evaluate(@twoscope)[0].type.should == "One::Two" + end + + it "should be able to generate resources with unqualified type information" do + resource("one").evaluate(@twoscope)[0].type.should == "One" + end + + it "should correctly generate resources that can look up builtin types" do + resource("file").evaluate(@twoscope)[0].type.should == "File" + end + + it "should fail for resource types that do not exist" do + lambda { resource("nosuchtype").evaluate(@twoscope) }.should raise_error(Puppet::ParseError) + end + end +end diff --git a/spec/unit/parser/ast/resource_spec_spec.rb b/spec/unit/parser/ast/resource_spec_spec.rb deleted file mode 100755 index 2473fda25..000000000 --- a/spec/unit/parser/ast/resource_spec_spec.rb +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::Resource do - ast = Puppet::Parser::AST - - before :each do - @title = Puppet::Parser::AST::String.new(:value => "mytitle") - @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) - @scope = Puppet::Parser::Scope.new(:compiler => @compiler) - @scope.stubs(:resource).returns(stub_everything) - @resource = ast::Resource.new(:title => @title, :type => "file", :parameters => ast::ASTArray.new(:children => []) ) - @resource.stubs(:qualified_type).returns("Resource") - end - - it "should evaluate all its parameters" do - param = stub 'param' - param.expects(:safeevaluate).with(@scope).returns Puppet::Parser::Resource::Param.new(:name => "myparam", :value => "myvalue", :source => stub("source")) - @resource.stubs(:parameters).returns [param] - - @resource.evaluate(@scope) - end - - it "should evaluate its title" do - @resource.evaluate(@scope)[0].title.should == "mytitle" - end - - it "should flatten the titles array" do - titles = [] - %w{one two}.each do |title| - titles << Puppet::Parser::AST::String.new(:value => title) - end - - array = Puppet::Parser::AST::ASTArray.new(:children => titles) - - @resource.title = array - result = @resource.evaluate(@scope).collect { |r| r.title } - result.should be_include("one") - result.should be_include("two") - end - - it "should create and return one resource objects per title" do - titles = [] - %w{one two}.each do |title| - titles << Puppet::Parser::AST::String.new(:value => title) - end - - array = Puppet::Parser::AST::ASTArray.new(:children => titles) - - @resource.title = array - result = @resource.evaluate(@scope).collect { |r| r.title } - result.should be_include("one") - result.should be_include("two") - end - - it "should handover resources to the compiler" do - titles = [] - %w{one two}.each do |title| - titles << Puppet::Parser::AST::String.new(:value => title) - end - - array = Puppet::Parser::AST::ASTArray.new(:children => titles) - - @resource.title = array - result = @resource.evaluate(@scope) - - result.each do |res| - @compiler.catalog.resource(res.ref).should be_instance_of(Puppet::Parser::Resource) - end - end - it "should generate virtual resources if it is virtual" do - @resource.virtual = true - - result = @resource.evaluate(@scope) - result[0].should be_virtual - end - - it "should generate virtual and exported resources if it is exported" do - @resource.exported = true - - result = @resource.evaluate(@scope) - result[0].should be_virtual - result[0].should be_exported - end - - # Related to #806, make sure resources always look up the full path to the resource. - describe "when generating qualified resources" do - before do - @scope = Puppet::Parser::Scope.new :compiler => Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) - @parser = Puppet::Parser::Parser.new(Puppet::Node::Environment.new) - @parser.newdefine "one" - @parser.newdefine "one::two" - @parser.newdefine "three" - @twoscope = @scope.newscope(:namespace => "one") - @twoscope.resource = @scope.resource - end - - def resource(type, params = nil) - params ||= Puppet::Parser::AST::ASTArray.new(:children => []) - Puppet::Parser::AST::Resource.new(:type => type, :title => Puppet::Parser::AST::String.new(:value => "myresource"), :parameters => params) - end - - it "should be able to generate resources with fully qualified type information" do - resource("two").evaluate(@twoscope)[0].type.should == "One::Two" - end - - it "should be able to generate resources with unqualified type information" do - resource("one").evaluate(@twoscope)[0].type.should == "One" - end - - it "should correctly generate resources that can look up builtin types" do - resource("file").evaluate(@twoscope)[0].type.should == "File" - end - - it "should fail for resource types that do not exist" do - lambda { resource("nosuchtype").evaluate(@twoscope) }.should raise_error(Puppet::ParseError) - end - end -end diff --git a/spec/unit/parser/ast/selector_spec.rb b/spec/unit/parser/ast/selector_spec.rb new file mode 100755 index 000000000..f32310b92 --- /dev/null +++ b/spec/unit/parser/ast/selector_spec.rb @@ -0,0 +1,139 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Selector do + before :each do + @scope = Puppet::Parser::Scope.new() + end + + describe "when evaluating" do + + before :each do + @param = stub 'param' + @param.stubs(:safeevaluate).with(@scope).returns("value") + + @value1 = stub 'value1' + @param1 = stub_everything 'param1' + @param1.stubs(:safeevaluate).with(@scope).returns(@param1) + @param1.stubs(:respond_to?).with(:downcase).returns(false) + @value1.stubs(:param).returns(@param1) + @value1.stubs(:value).returns(@value1) + + @value2 = stub 'value2' + @param2 = stub_everything 'param2' + @param2.stubs(:safeevaluate).with(@scope).returns(@param2) + @param2.stubs(:respond_to?).with(:downcase).returns(false) + @value2.stubs(:param).returns(@param2) + @value2.stubs(:value).returns(@value2) + + @values = stub 'values', :instance_of? => true + @values.stubs(:each).multiple_yields(@value1, @value2) + + @selector = Puppet::Parser::AST::Selector.new :param => @param, :values => @values + @selector.stubs(:fail) + end + + it "should evaluate param" do + @param.expects(:safeevaluate).with(@scope) + + @selector.evaluate(@scope) + end + + it "should scan each option" do + @values.expects(:each).multiple_yields(@value1, @value2) + + @selector.evaluate(@scope) + end + + describe "when scanning values" do + it "should evaluate first matching option" do + @param2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) + @value2.expects(:safeevaluate).with(@scope) + + @selector.evaluate(@scope) + end + + it "should return the first matching evaluated option" do + @param2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) + @value2.stubs(:safeevaluate).with(@scope).returns(:result) + + @selector.evaluate(@scope).should == :result + end + + it "should evaluate the default option if none matched" do + @param1.stubs(:is_a?).with(Puppet::Parser::AST::Default).returns(true) + @value1.expects(:safeevaluate).with(@scope).returns(@param1) + + @selector.evaluate(@scope) + end + + it "should return the default evaluated option if none matched" do + result = stub 'result' + @param1.stubs(:is_a?).with(Puppet::Parser::AST::Default).returns(true) + @value1.stubs(:safeevaluate).returns(result) + + @selector.evaluate(@scope).should == result + end + + it "should return nil if nothing matched" do + @selector.evaluate(@scope).should be_nil + end + + it "should delegate matching to evaluate_match" do + @param1.expects(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope } + + @selector.evaluate(@scope) + end + + it "should evaluate the matching param" do + @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) + + @value1.expects(:safeevaluate).with(@scope) + + @selector.evaluate(@scope) + end + + it "should return this evaluated option if it matches" do + @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) + @value1.stubs(:safeevaluate).with(@scope).returns(:result) + + @selector.evaluate(@scope).should == :result + end + + it "should unset scope ephemeral variables after option evaluation" do + @scope.stubs(:ephemeral_level).returns(:level) + @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) + @value1.stubs(:safeevaluate).with(@scope).returns(:result) + + @scope.expects(:unset_ephemeral_var).with(:level) + + @selector.evaluate(@scope) + end + + it "should not leak ephemeral variables even if evaluation fails" do + @scope.stubs(:ephemeral_level).returns(:level) + @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) + @value1.stubs(:safeevaluate).with(@scope).raises + + @scope.expects(:unset_ephemeral_var).with(:level) + + lambda { @selector.evaluate(@scope) }.should raise_error + end + + it "should fail if there is no default" do + @selector.expects(:fail) + + @selector.evaluate(@scope) + end + end + end + describe "when converting to string" do + it "should produce a string version of this selector" do + values = Puppet::Parser::AST::ASTArray.new :children => [ Puppet::Parser::AST::ResourceParam.new(:param => "type", :value => "value", :add => false) ] + param = Puppet::Parser::AST::Variable.new :value => "myvar" + selector = Puppet::Parser::AST::Selector.new :param => param, :values => values + selector.to_s.should == "$myvar ? { type => value }" + end + end +end diff --git a/spec/unit/parser/ast/selector_spec_spec.rb b/spec/unit/parser/ast/selector_spec_spec.rb deleted file mode 100755 index f32310b92..000000000 --- a/spec/unit/parser/ast/selector_spec_spec.rb +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::Selector do - before :each do - @scope = Puppet::Parser::Scope.new() - end - - describe "when evaluating" do - - before :each do - @param = stub 'param' - @param.stubs(:safeevaluate).with(@scope).returns("value") - - @value1 = stub 'value1' - @param1 = stub_everything 'param1' - @param1.stubs(:safeevaluate).with(@scope).returns(@param1) - @param1.stubs(:respond_to?).with(:downcase).returns(false) - @value1.stubs(:param).returns(@param1) - @value1.stubs(:value).returns(@value1) - - @value2 = stub 'value2' - @param2 = stub_everything 'param2' - @param2.stubs(:safeevaluate).with(@scope).returns(@param2) - @param2.stubs(:respond_to?).with(:downcase).returns(false) - @value2.stubs(:param).returns(@param2) - @value2.stubs(:value).returns(@value2) - - @values = stub 'values', :instance_of? => true - @values.stubs(:each).multiple_yields(@value1, @value2) - - @selector = Puppet::Parser::AST::Selector.new :param => @param, :values => @values - @selector.stubs(:fail) - end - - it "should evaluate param" do - @param.expects(:safeevaluate).with(@scope) - - @selector.evaluate(@scope) - end - - it "should scan each option" do - @values.expects(:each).multiple_yields(@value1, @value2) - - @selector.evaluate(@scope) - end - - describe "when scanning values" do - it "should evaluate first matching option" do - @param2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) - @value2.expects(:safeevaluate).with(@scope) - - @selector.evaluate(@scope) - end - - it "should return the first matching evaluated option" do - @param2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) - @value2.stubs(:safeevaluate).with(@scope).returns(:result) - - @selector.evaluate(@scope).should == :result - end - - it "should evaluate the default option if none matched" do - @param1.stubs(:is_a?).with(Puppet::Parser::AST::Default).returns(true) - @value1.expects(:safeevaluate).with(@scope).returns(@param1) - - @selector.evaluate(@scope) - end - - it "should return the default evaluated option if none matched" do - result = stub 'result' - @param1.stubs(:is_a?).with(Puppet::Parser::AST::Default).returns(true) - @value1.stubs(:safeevaluate).returns(result) - - @selector.evaluate(@scope).should == result - end - - it "should return nil if nothing matched" do - @selector.evaluate(@scope).should be_nil - end - - it "should delegate matching to evaluate_match" do - @param1.expects(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope } - - @selector.evaluate(@scope) - end - - it "should evaluate the matching param" do - @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) - - @value1.expects(:safeevaluate).with(@scope) - - @selector.evaluate(@scope) - end - - it "should return this evaluated option if it matches" do - @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) - @value1.stubs(:safeevaluate).with(@scope).returns(:result) - - @selector.evaluate(@scope).should == :result - end - - it "should unset scope ephemeral variables after option evaluation" do - @scope.stubs(:ephemeral_level).returns(:level) - @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) - @value1.stubs(:safeevaluate).with(@scope).returns(:result) - - @scope.expects(:unset_ephemeral_var).with(:level) - - @selector.evaluate(@scope) - end - - it "should not leak ephemeral variables even if evaluation fails" do - @scope.stubs(:ephemeral_level).returns(:level) - @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) - @value1.stubs(:safeevaluate).with(@scope).raises - - @scope.expects(:unset_ephemeral_var).with(:level) - - lambda { @selector.evaluate(@scope) }.should raise_error - end - - it "should fail if there is no default" do - @selector.expects(:fail) - - @selector.evaluate(@scope) - end - end - end - describe "when converting to string" do - it "should produce a string version of this selector" do - values = Puppet::Parser::AST::ASTArray.new :children => [ Puppet::Parser::AST::ResourceParam.new(:param => "type", :value => "value", :add => false) ] - param = Puppet::Parser::AST::Variable.new :value => "myvar" - selector = Puppet::Parser::AST::Selector.new :param => param, :values => values - selector.to_s.should == "$myvar ? { type => value }" - end - end -end diff --git a/spec/unit/parser/ast/vardef_spec.rb b/spec/unit/parser/ast/vardef_spec.rb new file mode 100755 index 000000000..a6863e75f --- /dev/null +++ b/spec/unit/parser/ast/vardef_spec.rb @@ -0,0 +1,60 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::VarDef do + before :each do + @scope = Puppet::Parser::Scope.new() + end + + describe "when evaluating" do + + it "should evaluate arguments" do + name = mock 'name' + value = mock 'value' + + name.expects(:safeevaluate).with(@scope) + value.expects(:safeevaluate).with(@scope) + + vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, + :line => nil + vardef.evaluate(@scope) + end + + it "should be in append=false mode if called without append" do + name = stub 'name', :safeevaluate => "var" + value = stub 'value', :safeevaluate => "1" + + @scope.expects(:setvar).with { |name,value,options| options[:append] == nil } + + vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, + :line => nil + vardef.evaluate(@scope) + end + + it "should call scope in append mode if append is true" do + name = stub 'name', :safeevaluate => "var" + value = stub 'value', :safeevaluate => "1" + + @scope.expects(:setvar).with { |name,value,options| options[:append] == true } + + vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, + :line => nil, :append => true + vardef.evaluate(@scope) + end + + describe "when dealing with hash" do + it "should delegate to the HashOrArrayAccess assign" do + access = stub 'name' + access.stubs(:is_a?).with(Puppet::Parser::AST::HashOrArrayAccess).returns(true) + value = stub 'value', :safeevaluate => "1" + vardef = Puppet::Parser::AST::VarDef.new :name => access, :value => value, :file => nil, :line => nil + + access.expects(:assign).with(@scope, '1') + + vardef.evaluate(@scope) + end + end + + end +end diff --git a/spec/unit/parser/ast/vardef_spec_spec.rb b/spec/unit/parser/ast/vardef_spec_spec.rb deleted file mode 100755 index a6863e75f..000000000 --- a/spec/unit/parser/ast/vardef_spec_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Parser::AST::VarDef do - before :each do - @scope = Puppet::Parser::Scope.new() - end - - describe "when evaluating" do - - it "should evaluate arguments" do - name = mock 'name' - value = mock 'value' - - name.expects(:safeevaluate).with(@scope) - value.expects(:safeevaluate).with(@scope) - - vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, - :line => nil - vardef.evaluate(@scope) - end - - it "should be in append=false mode if called without append" do - name = stub 'name', :safeevaluate => "var" - value = stub 'value', :safeevaluate => "1" - - @scope.expects(:setvar).with { |name,value,options| options[:append] == nil } - - vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, - :line => nil - vardef.evaluate(@scope) - end - - it "should call scope in append mode if append is true" do - name = stub 'name', :safeevaluate => "var" - value = stub 'value', :safeevaluate => "1" - - @scope.expects(:setvar).with { |name,value,options| options[:append] == true } - - vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, - :line => nil, :append => true - vardef.evaluate(@scope) - end - - describe "when dealing with hash" do - it "should delegate to the HashOrArrayAccess assign" do - access = stub 'name' - access.stubs(:is_a?).with(Puppet::Parser::AST::HashOrArrayAccess).returns(true) - value = stub 'value', :safeevaluate => "1" - vardef = Puppet::Parser::AST::VarDef.new :name => access, :value => value, :file => nil, :line => nil - - access.expects(:assign).with(@scope, '1') - - vardef.evaluate(@scope) - end - end - - end -end diff --git a/spec/unit/parser/ast_spec.rb b/spec/unit/parser/ast_spec.rb new file mode 100644 index 000000000..ca6f8080a --- /dev/null +++ b/spec/unit/parser/ast_spec.rb @@ -0,0 +1,41 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/parser/ast' + +describe Puppet::Parser::AST do + + it "should use the file lookup module" do + Puppet::Parser::AST.ancestors.should be_include(Puppet::FileCollection::Lookup) + end + + it "should have a doc accessor" do + ast = Puppet::Parser::AST.new({}) + ast.should respond_to(:doc) + end + + it "should have a use_docs accessor to indicate it wants documentation" do + ast = Puppet::Parser::AST.new({}) + ast.should respond_to(:use_docs) + end + + [ Puppet::Parser::AST::Collection, Puppet::Parser::AST::Else, + Puppet::Parser::AST::Function, Puppet::Parser::AST::IfStatement, + Puppet::Parser::AST::Resource, Puppet::Parser::AST::ResourceDefaults, + Puppet::Parser::AST::ResourceOverride, Puppet::Parser::AST::VarDef + ].each do |k| + it "#{k}.use_docs should return true" do + ast = k.new({}) + ast.use_docs.should be_true + end + end + + describe "when initializing" do + it "should store the doc argument if passed" do + ast = Puppet::Parser::AST.new(:doc => "documentation") + ast.doc.should == "documentation" + end + end + +end diff --git a/spec/unit/parser/ast_spec_spec.rb b/spec/unit/parser/ast_spec_spec.rb deleted file mode 100644 index ca6f8080a..000000000 --- a/spec/unit/parser/ast_spec_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/parser/ast' - -describe Puppet::Parser::AST do - - it "should use the file lookup module" do - Puppet::Parser::AST.ancestors.should be_include(Puppet::FileCollection::Lookup) - end - - it "should have a doc accessor" do - ast = Puppet::Parser::AST.new({}) - ast.should respond_to(:doc) - end - - it "should have a use_docs accessor to indicate it wants documentation" do - ast = Puppet::Parser::AST.new({}) - ast.should respond_to(:use_docs) - end - - [ Puppet::Parser::AST::Collection, Puppet::Parser::AST::Else, - Puppet::Parser::AST::Function, Puppet::Parser::AST::IfStatement, - Puppet::Parser::AST::Resource, Puppet::Parser::AST::ResourceDefaults, - Puppet::Parser::AST::ResourceOverride, Puppet::Parser::AST::VarDef - ].each do |k| - it "#{k}.use_docs should return true" do - ast = k.new({}) - ast.use_docs.should be_true - end - end - - describe "when initializing" do - it "should store the doc argument if passed" do - ast = Puppet::Parser::AST.new(:doc => "documentation") - ast.doc.should == "documentation" - end - end - -end diff --git a/spec/unit/parser/collector_spec.rb b/spec/unit/parser/collector_spec.rb new file mode 100755 index 000000000..de5fc8015 --- /dev/null +++ b/spec/unit/parser/collector_spec.rb @@ -0,0 +1,559 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/parser/collector' + +describe Puppet::Parser::Collector, "when initializing" do + before do + @scope = mock 'scope' + @resource_type = 'resource_type' + @form = :exported + @vquery = mock 'vquery' + @equery = mock 'equery' + + @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, @form) + end + + it "should require a scope" do + @collector.scope.should equal(@scope) + end + + it "should require a resource type" do + @collector.type.should == 'Resource_type' + end + + it "should only accept :virtual or :exported as the collector form" do + proc { @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @vquery, @equery, :other) }.should raise_error(ArgumentError) + end + + it "should accept an optional virtual query" do + @collector.vquery.should equal(@vquery) + end + + it "should accept an optional exported query" do + @collector.equery.should equal(@equery) + end + + it "should canonize the type name" do + @collector = Puppet::Parser::Collector.new(@scope, "resource::type", @equery, @vquery, @form) + @collector.type.should == "Resource::Type" + end + + it "should accept an optional resource override" do + @collector = Puppet::Parser::Collector.new(@scope, "resource::type", @equery, @vquery, @form) + override = { :parameters => "whatever" } + @collector.add_override(override) + @collector.overrides.should equal(override) + end + +end + +describe Puppet::Parser::Collector, "when collecting specific virtual resources" do + before do + @scope = mock 'scope' + @vquery = mock 'vquery' + @equery = mock 'equery' + + @collector = Puppet::Parser::Collector.new(@scope, "resource_type", @equery, @vquery, :virtual) + end + + it "should not fail when it does not find any resources to collect" do + @collector.resources = ["File[virtual1]", "File[virtual2]"] + @scope.stubs(:findresource).returns(false) + proc { @collector.evaluate }.should_not raise_error + end + + it "should mark matched resources as non-virtual" do + @collector.resources = ["File[virtual1]", "File[virtual2]"] + one = stub_everything 'one' + one.expects(:virtual=).with(false) + + @scope.stubs(:findresource).with("File[virtual1]").returns(one) + @scope.stubs(:findresource).with("File[virtual2]").returns(nil) + @collector.evaluate + end + + it "should return matched resources" do + @collector.resources = ["File[virtual1]", "File[virtual2]"] + one = stub_everything 'one' + @scope.stubs(:findresource).with("File[virtual1]").returns(one) + @scope.stubs(:findresource).with("File[virtual2]").returns(nil) + @collector.evaluate.should == [one] + end + + it "should delete itself from the compile's collection list if it has found all of its resources" do + @collector.resources = ["File[virtual1]"] + one = stub_everything 'one' + @compiler.expects(:delete_collection).with(@collector) + @scope.expects(:compiler).returns(@compiler) + @scope.stubs(:findresource).with("File[virtual1]").returns(one) + @collector.evaluate + end + + it "should not delete itself from the compile's collection list if it has unfound resources" do + @collector.resources = ["File[virtual1]"] + one = stub_everything 'one' + @compiler.expects(:delete_collection).never + @scope.stubs(:findresource).with("File[virtual1]").returns(nil) + @collector.evaluate + end +end + +describe Puppet::Parser::Collector, "when collecting virtual and catalog resources" do + before do + @scope = mock 'scope' + @compiler = mock 'compile' + @scope.stubs(:compiler).returns(@compiler) + @resource_type = "Mytype" + @vquery = proc { |res| true } + + @collector = Puppet::Parser::Collector.new(@scope, @resource_type, nil, @vquery, :virtual) + end + + it "should find all virtual resources matching the vquery" do + one = stub_everything 'one', :type => "Mytype", :virtual? => true + two = stub_everything 'two', :type => "Mytype", :virtual? => true + + @compiler.expects(:resources).returns([one, two]) + + @collector.evaluate.should == [one, two] + end + + it "should find all non-virtual resources matching the vquery" do + one = stub_everything 'one', :type => "Mytype", :virtual? => false + two = stub_everything 'two', :type => "Mytype", :virtual? => false + + @compiler.expects(:resources).returns([one, two]) + + @collector.evaluate.should == [one, two] + end + + it "should mark all matched resources as non-virtual" do + one = stub_everything 'one', :type => "Mytype", :virtual? => true + + one.expects(:virtual=).with(false) + + @compiler.expects(:resources).returns([one]) + + @collector.evaluate + end + + it "should return matched resources" do + one = stub_everything 'one', :type => "Mytype", :virtual? => true + two = stub_everything 'two', :type => "Mytype", :virtual? => true + + @compiler.expects(:resources).returns([one, two]) + + @collector.evaluate.should == [one, two] + end + + it "should return all resources of the correct type if there is no virtual query" do + one = stub_everything 'one', :type => "Mytype", :virtual? => true + two = stub_everything 'two', :type => "Mytype", :virtual? => true + + one.expects(:virtual=).with(false) + two.expects(:virtual=).with(false) + + @compiler.expects(:resources).returns([one, two]) + + @collector = Puppet::Parser::Collector.new(@scope, @resource_type, nil, nil, :virtual) + + @collector.evaluate.should == [one, two] + end + + it "should not return or mark resources of a different type" do + one = stub_everything 'one', :type => "Mytype", :virtual? => true + two = stub_everything 'two', :type => :other, :virtual? => true + + one.expects(:virtual=).with(false) + two.expects(:virtual=).never + + @compiler.expects(:resources).returns([one, two]) + + @collector.evaluate.should == [one] + end + + it "should create a resource with overridden parameters" do + one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" + param = stub 'param' + @compiler.stubs(:add_override) + + @compiler.expects(:resources).returns([one]) + + @collector.add_override(:parameters => param ) + Puppet::Parser::Resource.expects(:new).with { |type, title, h| + h[:parameters] == param + } + + @collector.evaluate + end + + it "should define a new allow all child_of? on overriden resource" do + one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" + param = stub 'param' + source = stub 'source' + @compiler.stubs(:add_override) + + @compiler.expects(:resources).returns([one]) + + @collector.add_override(:parameters => param, :source => source ) + Puppet::Parser::Resource.stubs(:new) + + source.expects(:meta_def).with { |name,block| name == :child_of? } + + @collector.evaluate + end + + + it "should not override already overriden resources for this same collection in a previous run" do + one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" + param = stub 'param' + @compiler.stubs(:add_override) + + @compiler.expects(:resources).at_least(2).returns([one]) + + @collector.add_override(:parameters => param ) + Puppet::Parser::Resource.expects(:new).once.with { |type, title, h| + h[:parameters] == param + } + + @collector.evaluate + + @collector.evaluate + end + + it "should not return resources that were collected in a previous run of this collector" do + one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" + @compiler.stubs(:resources).returns([one]) + + @collector.evaluate + + @collector.evaluate.should be_false + end + + + it "should tell the compiler about the overriden resources" do + one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" + param = stub 'param' + + one.expects(:virtual=).with(false) + @compiler.expects(:resources).returns([one]) + @collector.add_override(:parameters => param ) + Puppet::Parser::Resource.stubs(:new).returns("whatever") + + @compiler.expects(:add_override).with("whatever") + + @collector.evaluate + end + + it "should not return or mark non-matching resources" do + @collector.vquery = proc { |res| res.name == :one } + + one = stub_everything 'one', :name => :one, :type => "Mytype", :virtual? => true + two = stub_everything 'two', :name => :two, :type => "Mytype", :virtual? => true + + one.expects(:virtual=).with(false) + two.expects(:virtual=).never + + @compiler.expects(:resources).returns([one, two]) + + @collector.evaluate.should == [one] + end +end + +describe Puppet::Parser::Collector, "when collecting exported resources" do + confine "Cannot test Rails integration without ActiveRecord" => Puppet.features.rails? + + before do + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) + @scope = Puppet::Parser::Scope.new :compiler => @compiler + @resource_type = "Mytype" + @equery = "test = true" + @vquery = proc { |r| true } + + res = stub("resource 1") + res.stubs(:type).returns @resource_type + Puppet::Resource.stubs(:new).returns res + + Puppet.settings.stubs(:value).with(:storeconfigs).returns true + Puppet.settings.stubs(:value).with(:environment).returns "production" + + @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, :exported) + end + + # Stub most of our interface to Rails. + def stub_rails(everything = false) + ActiveRecord::Base.stubs(:connected?).returns(false) + Puppet::Rails.stubs(:init) + if everything + Puppet::Rails::Host.stubs(:find_by_name).returns(nil) + Puppet::Rails::Resource.stubs(:find).returns([]) + end + end + + it "should just return false if :storeconfigs is not enabled" do + Puppet.settings.expects(:value).with(:storeconfigs).returns false + @collector.evaluate.should be_false + end + + it "should use initialize the Rails support if ActiveRecord is not connected" do + @compiler.stubs(:resources).returns([]) + ActiveRecord::Base.expects(:connected?).returns(false) + Puppet::Rails.expects(:init) + Puppet::Rails::Host.stubs(:find_by_name).returns(nil) + Puppet::Rails::Resource.stubs(:find).returns([]) + + @collector.evaluate + end + + it "should return all matching resources from the current compile and mark them non-virtual and non-exported" do + stub_rails(true) + + one = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :ref => "one" + two = stub 'two', :type => "Mytype", :virtual? => true, :exported? => true, :ref => "two" + + one.stubs(:exported=) + one.stubs(:virtual=) + two.stubs(:exported=) + two.stubs(:virtual=) + + @compiler.expects(:resources).returns([one, two]) + + @collector.evaluate.should == [one, two] + end + + it "should mark all returned resources as not virtual" do + stub_rails(true) + + one = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :ref => "one" + + one.stubs(:exported=) + one.expects(:virtual=).with(false) + + @compiler.expects(:resources).returns([one]) + + @collector.evaluate.should == [one] + end + + it "should convert all found resources into parser resources" do + stub_rails() + Puppet::Rails::Host.stubs(:find_by_name).returns(nil) + + one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" + Puppet::Rails::Resource.stubs(:find).returns([one]) + + resource = mock 'resource' + one.expects(:to_resource).with(@scope).returns(resource) + resource.stubs(:exported=) + resource.stubs(:virtual=) + resource.stubs(:ref) + + @compiler.stubs(:resources).returns([]) + @scope.stubs(:findresource).returns(nil) + + @compiler.stubs(:add_resource) + + @collector.evaluate.should == [resource] + end + + it "should override all exported collected resources if collector has an override" do + stub_rails() + Puppet::Rails::Host.stubs(:find_by_name).returns(nil) + + one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" + Puppet::Rails::Resource.stubs(:find).returns([one]) + + resource = mock 'resource', :type => "Mytype" + one.expects(:to_resource).with(@scope).returns(resource) + resource.stubs(:exported=) + resource.stubs(:virtual=) + resource.stubs(:ref) + resource.stubs(:title) + + @compiler.stubs(:resources).returns([]) + @scope.stubs(:findresource).returns(nil) + + param = stub 'param' + @compiler.stubs(:add_override) + @compiler.stubs(:add_resource) + + @collector.add_override(:parameters => param ) + Puppet::Parser::Resource.expects(:new).once.with { |type, title, h| + h[:parameters] == param + } + + @collector.evaluate + end + + it "should store converted resources in the compile's resource list" do + stub_rails() + Puppet::Rails::Host.stubs(:find_by_name).returns(nil) + + one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" + Puppet::Rails::Resource.stubs(:find).returns([one]) + + resource = mock 'resource' + one.expects(:to_resource).with(@scope).returns(resource) + resource.stubs(:exported=) + resource.stubs(:virtual=) + resource.stubs(:ref) + + @compiler.stubs(:resources).returns([]) + @scope.stubs(:findresource).returns(nil) + + @compiler.expects(:add_resource).with(@scope, resource) + + @collector.evaluate.should == [resource] + end + + # This way one host doesn't store another host's resources as exported. + it "should mark resources collected from the database as not exported" do + stub_rails() + Puppet::Rails::Host.stubs(:find_by_name).returns(nil) + + one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" + Puppet::Rails::Resource.stubs(:find).returns([one]) + + resource = mock 'resource' + one.expects(:to_resource).with(@scope).returns(resource) + resource.expects(:exported=).with(false) + resource.stubs(:virtual=) + resource.stubs(:ref) + + @compiler.stubs(:resources).returns([]) + @scope.stubs(:findresource).returns(nil) + + @compiler.stubs(:add_resource) + + @collector.evaluate + end + + it "should fail if an equivalent resource already exists in the compile" do + stub_rails() + Puppet::Rails::Host.stubs(:find_by_name).returns(nil) + + rails = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :id => 1, :ref => "yay" + inmemory = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :rails_id => 2 + + Puppet::Rails::Resource.stubs(:find).returns([rails]) + + resource = mock 'resource' + + @compiler.stubs(:resources).returns([]) + @scope.stubs(:findresource).returns(inmemory) + + @compiler.stubs(:add_resource) + + proc { @collector.evaluate }.should raise_error(Puppet::ParseError) + end + + it "should ignore exported resources that match already-collected resources" do + stub_rails() + Puppet::Rails::Host.stubs(:find_by_name).returns(nil) + + rails = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :id => 1, :ref => "yay" + inmemory = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :rails_id => 1 + + Puppet::Rails::Resource.stubs(:find).returns([rails]) + + resource = mock 'resource' + + @compiler.stubs(:resources).returns([]) + @scope.stubs(:findresource).returns(inmemory) + + @compiler.stubs(:add_resource) + + proc { @collector.evaluate }.should_not raise_error(Puppet::ParseError) + end +end + +describe Puppet::Parser::Collector, "when building its ActiveRecord query for collecting exported resources" do + confine "Cannot test Rails integration without ActiveRecord" => Puppet.features.rails? + + before do + @scope = stub 'scope', :host => "myhost", :debug => nil + @compiler = mock 'compile' + @scope.stubs(:compiler).returns(@compiler) + @resource_type = "Mytype" + @equery = nil + @vquery = proc { |r| true } + + @resource = stub_everything 'collected' + + @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, :exported) + @collector.stubs(:exported_resource).with(@resource).returns(@resource) + @compiler.stubs(:resources).returns([]) + + ActiveRecord::Base.stubs(:connected?).returns(false) + + Puppet::Rails.stubs(:init) + Puppet::Rails::Host.stubs(:find_by_name).returns(nil) + Puppet::Rails::Resource.stubs(:find).returns([]) + + Puppet.settings.stubs(:value).with(:storeconfigs).returns true + end + + it "should exclude all resources from the host if ActiveRecord contains information for this host" do + @host = mock 'host' + @host.stubs(:id).returns 5 + + Puppet::Rails::Host.expects(:find_by_name).with(@scope.host).returns(@host) + + Puppet::Rails::Resource.stubs(:find).with { |*arguments| + options = arguments[1] + options[:conditions][0] =~ /^host_id != \?/ and options[:conditions][1] == 5 + }.returns([@resource]) + + @collector.evaluate.should == [@resource] + end + + it "should join with parameter names, parameter values when querying ActiveRecord" do + @collector.equery = "param_names.name = title" + Puppet::Rails::Resource.stubs(:find).with { |*arguments| + options = arguments[1] + options[:joins] == {:param_values => :param_name} + }.returns([@resource]) + + @collector.evaluate.should == [@resource] + end + + it "should join with tag tables when querying ActiveRecord with a tag exported query" do + @collector.equery = "puppet_tags.name = test" + Puppet::Rails::Resource.stubs(:find).with { |*arguments| + options = arguments[1] + options[:joins] == {:resource_tags => :puppet_tag} + }.returns([@resource]) + + @collector.evaluate.should == [@resource] + end + + it "should not join parameters when querying ActiveRecord with a tag exported query" do + @collector.equery = "puppet_tags.name = test" + Puppet::Rails::Resource.stubs(:find).with { |*arguments| + options = arguments[1] + options[:joins] == {:param_values => :param_name} + }.returns([@resource]) + + @collector.evaluate.should be_false + end + + it "should only search for exported resources with the matching type" do + Puppet::Rails::Resource.stubs(:find).with { |*arguments| + options = arguments[1] + options[:conditions][0].include?("(exported=? AND restype=?)") and options[:conditions][1] == true and options[:conditions][2] == "Mytype" + }.returns([@resource]) + + @collector.evaluate.should == [@resource] + end + + it "should include the export query if one is provided" do + @collector.equery = "test = true" + Puppet::Rails::Resource.stubs(:find).with { |*arguments| + options = arguments[1] + options[:conditions][0].include?("test = true") + }.returns([@resource]) + + @collector.evaluate.should == [@resource] + end +end diff --git a/spec/unit/parser/collector_spec_spec.rb b/spec/unit/parser/collector_spec_spec.rb deleted file mode 100755 index de5fc8015..000000000 --- a/spec/unit/parser/collector_spec_spec.rb +++ /dev/null @@ -1,559 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/parser/collector' - -describe Puppet::Parser::Collector, "when initializing" do - before do - @scope = mock 'scope' - @resource_type = 'resource_type' - @form = :exported - @vquery = mock 'vquery' - @equery = mock 'equery' - - @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, @form) - end - - it "should require a scope" do - @collector.scope.should equal(@scope) - end - - it "should require a resource type" do - @collector.type.should == 'Resource_type' - end - - it "should only accept :virtual or :exported as the collector form" do - proc { @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @vquery, @equery, :other) }.should raise_error(ArgumentError) - end - - it "should accept an optional virtual query" do - @collector.vquery.should equal(@vquery) - end - - it "should accept an optional exported query" do - @collector.equery.should equal(@equery) - end - - it "should canonize the type name" do - @collector = Puppet::Parser::Collector.new(@scope, "resource::type", @equery, @vquery, @form) - @collector.type.should == "Resource::Type" - end - - it "should accept an optional resource override" do - @collector = Puppet::Parser::Collector.new(@scope, "resource::type", @equery, @vquery, @form) - override = { :parameters => "whatever" } - @collector.add_override(override) - @collector.overrides.should equal(override) - end - -end - -describe Puppet::Parser::Collector, "when collecting specific virtual resources" do - before do - @scope = mock 'scope' - @vquery = mock 'vquery' - @equery = mock 'equery' - - @collector = Puppet::Parser::Collector.new(@scope, "resource_type", @equery, @vquery, :virtual) - end - - it "should not fail when it does not find any resources to collect" do - @collector.resources = ["File[virtual1]", "File[virtual2]"] - @scope.stubs(:findresource).returns(false) - proc { @collector.evaluate }.should_not raise_error - end - - it "should mark matched resources as non-virtual" do - @collector.resources = ["File[virtual1]", "File[virtual2]"] - one = stub_everything 'one' - one.expects(:virtual=).with(false) - - @scope.stubs(:findresource).with("File[virtual1]").returns(one) - @scope.stubs(:findresource).with("File[virtual2]").returns(nil) - @collector.evaluate - end - - it "should return matched resources" do - @collector.resources = ["File[virtual1]", "File[virtual2]"] - one = stub_everything 'one' - @scope.stubs(:findresource).with("File[virtual1]").returns(one) - @scope.stubs(:findresource).with("File[virtual2]").returns(nil) - @collector.evaluate.should == [one] - end - - it "should delete itself from the compile's collection list if it has found all of its resources" do - @collector.resources = ["File[virtual1]"] - one = stub_everything 'one' - @compiler.expects(:delete_collection).with(@collector) - @scope.expects(:compiler).returns(@compiler) - @scope.stubs(:findresource).with("File[virtual1]").returns(one) - @collector.evaluate - end - - it "should not delete itself from the compile's collection list if it has unfound resources" do - @collector.resources = ["File[virtual1]"] - one = stub_everything 'one' - @compiler.expects(:delete_collection).never - @scope.stubs(:findresource).with("File[virtual1]").returns(nil) - @collector.evaluate - end -end - -describe Puppet::Parser::Collector, "when collecting virtual and catalog resources" do - before do - @scope = mock 'scope' - @compiler = mock 'compile' - @scope.stubs(:compiler).returns(@compiler) - @resource_type = "Mytype" - @vquery = proc { |res| true } - - @collector = Puppet::Parser::Collector.new(@scope, @resource_type, nil, @vquery, :virtual) - end - - it "should find all virtual resources matching the vquery" do - one = stub_everything 'one', :type => "Mytype", :virtual? => true - two = stub_everything 'two', :type => "Mytype", :virtual? => true - - @compiler.expects(:resources).returns([one, two]) - - @collector.evaluate.should == [one, two] - end - - it "should find all non-virtual resources matching the vquery" do - one = stub_everything 'one', :type => "Mytype", :virtual? => false - two = stub_everything 'two', :type => "Mytype", :virtual? => false - - @compiler.expects(:resources).returns([one, two]) - - @collector.evaluate.should == [one, two] - end - - it "should mark all matched resources as non-virtual" do - one = stub_everything 'one', :type => "Mytype", :virtual? => true - - one.expects(:virtual=).with(false) - - @compiler.expects(:resources).returns([one]) - - @collector.evaluate - end - - it "should return matched resources" do - one = stub_everything 'one', :type => "Mytype", :virtual? => true - two = stub_everything 'two', :type => "Mytype", :virtual? => true - - @compiler.expects(:resources).returns([one, two]) - - @collector.evaluate.should == [one, two] - end - - it "should return all resources of the correct type if there is no virtual query" do - one = stub_everything 'one', :type => "Mytype", :virtual? => true - two = stub_everything 'two', :type => "Mytype", :virtual? => true - - one.expects(:virtual=).with(false) - two.expects(:virtual=).with(false) - - @compiler.expects(:resources).returns([one, two]) - - @collector = Puppet::Parser::Collector.new(@scope, @resource_type, nil, nil, :virtual) - - @collector.evaluate.should == [one, two] - end - - it "should not return or mark resources of a different type" do - one = stub_everything 'one', :type => "Mytype", :virtual? => true - two = stub_everything 'two', :type => :other, :virtual? => true - - one.expects(:virtual=).with(false) - two.expects(:virtual=).never - - @compiler.expects(:resources).returns([one, two]) - - @collector.evaluate.should == [one] - end - - it "should create a resource with overridden parameters" do - one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" - param = stub 'param' - @compiler.stubs(:add_override) - - @compiler.expects(:resources).returns([one]) - - @collector.add_override(:parameters => param ) - Puppet::Parser::Resource.expects(:new).with { |type, title, h| - h[:parameters] == param - } - - @collector.evaluate - end - - it "should define a new allow all child_of? on overriden resource" do - one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" - param = stub 'param' - source = stub 'source' - @compiler.stubs(:add_override) - - @compiler.expects(:resources).returns([one]) - - @collector.add_override(:parameters => param, :source => source ) - Puppet::Parser::Resource.stubs(:new) - - source.expects(:meta_def).with { |name,block| name == :child_of? } - - @collector.evaluate - end - - - it "should not override already overriden resources for this same collection in a previous run" do - one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" - param = stub 'param' - @compiler.stubs(:add_override) - - @compiler.expects(:resources).at_least(2).returns([one]) - - @collector.add_override(:parameters => param ) - Puppet::Parser::Resource.expects(:new).once.with { |type, title, h| - h[:parameters] == param - } - - @collector.evaluate - - @collector.evaluate - end - - it "should not return resources that were collected in a previous run of this collector" do - one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" - @compiler.stubs(:resources).returns([one]) - - @collector.evaluate - - @collector.evaluate.should be_false - end - - - it "should tell the compiler about the overriden resources" do - one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" - param = stub 'param' - - one.expects(:virtual=).with(false) - @compiler.expects(:resources).returns([one]) - @collector.add_override(:parameters => param ) - Puppet::Parser::Resource.stubs(:new).returns("whatever") - - @compiler.expects(:add_override).with("whatever") - - @collector.evaluate - end - - it "should not return or mark non-matching resources" do - @collector.vquery = proc { |res| res.name == :one } - - one = stub_everything 'one', :name => :one, :type => "Mytype", :virtual? => true - two = stub_everything 'two', :name => :two, :type => "Mytype", :virtual? => true - - one.expects(:virtual=).with(false) - two.expects(:virtual=).never - - @compiler.expects(:resources).returns([one, two]) - - @collector.evaluate.should == [one] - end -end - -describe Puppet::Parser::Collector, "when collecting exported resources" do - confine "Cannot test Rails integration without ActiveRecord" => Puppet.features.rails? - - before do - @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) - @scope = Puppet::Parser::Scope.new :compiler => @compiler - @resource_type = "Mytype" - @equery = "test = true" - @vquery = proc { |r| true } - - res = stub("resource 1") - res.stubs(:type).returns @resource_type - Puppet::Resource.stubs(:new).returns res - - Puppet.settings.stubs(:value).with(:storeconfigs).returns true - Puppet.settings.stubs(:value).with(:environment).returns "production" - - @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, :exported) - end - - # Stub most of our interface to Rails. - def stub_rails(everything = false) - ActiveRecord::Base.stubs(:connected?).returns(false) - Puppet::Rails.stubs(:init) - if everything - Puppet::Rails::Host.stubs(:find_by_name).returns(nil) - Puppet::Rails::Resource.stubs(:find).returns([]) - end - end - - it "should just return false if :storeconfigs is not enabled" do - Puppet.settings.expects(:value).with(:storeconfigs).returns false - @collector.evaluate.should be_false - end - - it "should use initialize the Rails support if ActiveRecord is not connected" do - @compiler.stubs(:resources).returns([]) - ActiveRecord::Base.expects(:connected?).returns(false) - Puppet::Rails.expects(:init) - Puppet::Rails::Host.stubs(:find_by_name).returns(nil) - Puppet::Rails::Resource.stubs(:find).returns([]) - - @collector.evaluate - end - - it "should return all matching resources from the current compile and mark them non-virtual and non-exported" do - stub_rails(true) - - one = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :ref => "one" - two = stub 'two', :type => "Mytype", :virtual? => true, :exported? => true, :ref => "two" - - one.stubs(:exported=) - one.stubs(:virtual=) - two.stubs(:exported=) - two.stubs(:virtual=) - - @compiler.expects(:resources).returns([one, two]) - - @collector.evaluate.should == [one, two] - end - - it "should mark all returned resources as not virtual" do - stub_rails(true) - - one = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :ref => "one" - - one.stubs(:exported=) - one.expects(:virtual=).with(false) - - @compiler.expects(:resources).returns([one]) - - @collector.evaluate.should == [one] - end - - it "should convert all found resources into parser resources" do - stub_rails() - Puppet::Rails::Host.stubs(:find_by_name).returns(nil) - - one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" - Puppet::Rails::Resource.stubs(:find).returns([one]) - - resource = mock 'resource' - one.expects(:to_resource).with(@scope).returns(resource) - resource.stubs(:exported=) - resource.stubs(:virtual=) - resource.stubs(:ref) - - @compiler.stubs(:resources).returns([]) - @scope.stubs(:findresource).returns(nil) - - @compiler.stubs(:add_resource) - - @collector.evaluate.should == [resource] - end - - it "should override all exported collected resources if collector has an override" do - stub_rails() - Puppet::Rails::Host.stubs(:find_by_name).returns(nil) - - one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" - Puppet::Rails::Resource.stubs(:find).returns([one]) - - resource = mock 'resource', :type => "Mytype" - one.expects(:to_resource).with(@scope).returns(resource) - resource.stubs(:exported=) - resource.stubs(:virtual=) - resource.stubs(:ref) - resource.stubs(:title) - - @compiler.stubs(:resources).returns([]) - @scope.stubs(:findresource).returns(nil) - - param = stub 'param' - @compiler.stubs(:add_override) - @compiler.stubs(:add_resource) - - @collector.add_override(:parameters => param ) - Puppet::Parser::Resource.expects(:new).once.with { |type, title, h| - h[:parameters] == param - } - - @collector.evaluate - end - - it "should store converted resources in the compile's resource list" do - stub_rails() - Puppet::Rails::Host.stubs(:find_by_name).returns(nil) - - one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" - Puppet::Rails::Resource.stubs(:find).returns([one]) - - resource = mock 'resource' - one.expects(:to_resource).with(@scope).returns(resource) - resource.stubs(:exported=) - resource.stubs(:virtual=) - resource.stubs(:ref) - - @compiler.stubs(:resources).returns([]) - @scope.stubs(:findresource).returns(nil) - - @compiler.expects(:add_resource).with(@scope, resource) - - @collector.evaluate.should == [resource] - end - - # This way one host doesn't store another host's resources as exported. - it "should mark resources collected from the database as not exported" do - stub_rails() - Puppet::Rails::Host.stubs(:find_by_name).returns(nil) - - one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" - Puppet::Rails::Resource.stubs(:find).returns([one]) - - resource = mock 'resource' - one.expects(:to_resource).with(@scope).returns(resource) - resource.expects(:exported=).with(false) - resource.stubs(:virtual=) - resource.stubs(:ref) - - @compiler.stubs(:resources).returns([]) - @scope.stubs(:findresource).returns(nil) - - @compiler.stubs(:add_resource) - - @collector.evaluate - end - - it "should fail if an equivalent resource already exists in the compile" do - stub_rails() - Puppet::Rails::Host.stubs(:find_by_name).returns(nil) - - rails = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :id => 1, :ref => "yay" - inmemory = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :rails_id => 2 - - Puppet::Rails::Resource.stubs(:find).returns([rails]) - - resource = mock 'resource' - - @compiler.stubs(:resources).returns([]) - @scope.stubs(:findresource).returns(inmemory) - - @compiler.stubs(:add_resource) - - proc { @collector.evaluate }.should raise_error(Puppet::ParseError) - end - - it "should ignore exported resources that match already-collected resources" do - stub_rails() - Puppet::Rails::Host.stubs(:find_by_name).returns(nil) - - rails = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :id => 1, :ref => "yay" - inmemory = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :rails_id => 1 - - Puppet::Rails::Resource.stubs(:find).returns([rails]) - - resource = mock 'resource' - - @compiler.stubs(:resources).returns([]) - @scope.stubs(:findresource).returns(inmemory) - - @compiler.stubs(:add_resource) - - proc { @collector.evaluate }.should_not raise_error(Puppet::ParseError) - end -end - -describe Puppet::Parser::Collector, "when building its ActiveRecord query for collecting exported resources" do - confine "Cannot test Rails integration without ActiveRecord" => Puppet.features.rails? - - before do - @scope = stub 'scope', :host => "myhost", :debug => nil - @compiler = mock 'compile' - @scope.stubs(:compiler).returns(@compiler) - @resource_type = "Mytype" - @equery = nil - @vquery = proc { |r| true } - - @resource = stub_everything 'collected' - - @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, :exported) - @collector.stubs(:exported_resource).with(@resource).returns(@resource) - @compiler.stubs(:resources).returns([]) - - ActiveRecord::Base.stubs(:connected?).returns(false) - - Puppet::Rails.stubs(:init) - Puppet::Rails::Host.stubs(:find_by_name).returns(nil) - Puppet::Rails::Resource.stubs(:find).returns([]) - - Puppet.settings.stubs(:value).with(:storeconfigs).returns true - end - - it "should exclude all resources from the host if ActiveRecord contains information for this host" do - @host = mock 'host' - @host.stubs(:id).returns 5 - - Puppet::Rails::Host.expects(:find_by_name).with(@scope.host).returns(@host) - - Puppet::Rails::Resource.stubs(:find).with { |*arguments| - options = arguments[1] - options[:conditions][0] =~ /^host_id != \?/ and options[:conditions][1] == 5 - }.returns([@resource]) - - @collector.evaluate.should == [@resource] - end - - it "should join with parameter names, parameter values when querying ActiveRecord" do - @collector.equery = "param_names.name = title" - Puppet::Rails::Resource.stubs(:find).with { |*arguments| - options = arguments[1] - options[:joins] == {:param_values => :param_name} - }.returns([@resource]) - - @collector.evaluate.should == [@resource] - end - - it "should join with tag tables when querying ActiveRecord with a tag exported query" do - @collector.equery = "puppet_tags.name = test" - Puppet::Rails::Resource.stubs(:find).with { |*arguments| - options = arguments[1] - options[:joins] == {:resource_tags => :puppet_tag} - }.returns([@resource]) - - @collector.evaluate.should == [@resource] - end - - it "should not join parameters when querying ActiveRecord with a tag exported query" do - @collector.equery = "puppet_tags.name = test" - Puppet::Rails::Resource.stubs(:find).with { |*arguments| - options = arguments[1] - options[:joins] == {:param_values => :param_name} - }.returns([@resource]) - - @collector.evaluate.should be_false - end - - it "should only search for exported resources with the matching type" do - Puppet::Rails::Resource.stubs(:find).with { |*arguments| - options = arguments[1] - options[:conditions][0].include?("(exported=? AND restype=?)") and options[:conditions][1] == true and options[:conditions][2] == "Mytype" - }.returns([@resource]) - - @collector.evaluate.should == [@resource] - end - - it "should include the export query if one is provided" do - @collector.equery = "test = true" - Puppet::Rails::Resource.stubs(:find).with { |*arguments| - options = arguments[1] - options[:conditions][0].include?("test = true") - }.returns([@resource]) - - @collector.evaluate.should == [@resource] - end -end diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb new file mode 100755 index 000000000..67bfc3749 --- /dev/null +++ b/spec/unit/parser/compiler_spec.rb @@ -0,0 +1,753 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +class CompilerTestResource + attr_accessor :builtin, :virtual, :evaluated, :type, :title + + def initialize(type, title) + @type = type + @title = title + end + + def [](attr) + return nil if attr == :stage + :main + end + + def ref + "%s[%s]" % [type.to_s.capitalize, title] + end + + def evaluated? + @evaluated + end + + def builtin? + @builtin + end + + def virtual? + @virtual + end + + def evaluate + end +end + +describe Puppet::Parser::Compiler do + def resource(type, title) + Puppet::Parser::Resource.new(type, title, :scope => @scope) + end + + before :each do + @node = Puppet::Node.new "testnode" + @known_resource_types = Puppet::Resource::TypeCollection.new "development" + @compiler = Puppet::Parser::Compiler.new(@node) + @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => stub('source')) + @scope_resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope) + @scope.resource = @scope_resource + @compiler.environment.stubs(:known_resource_types).returns @known_resource_types + end + + it "should have a class method that compiles, converts, and returns a catalog" do + compiler = stub 'compiler' + Puppet::Parser::Compiler.expects(:new).with(@node).returns compiler + catalog = stub 'catalog' + compiler.expects(:compile).returns catalog + converted_catalog = stub 'converted_catalog' + catalog.expects(:to_resource).returns converted_catalog + + Puppet::Parser::Compiler.compile(@node).should equal(converted_catalog) + end + + it "should fail intelligently when a class-level compile fails" do + Puppet::Parser::Compiler.expects(:new).raises ArgumentError + lambda { Puppet::Parser::Compiler.compile(@node) }.should raise_error(Puppet::Error) + end + + it "should use the node's environment as its environment" do + @compiler.environment.should equal(@node.environment) + end + + it "should include the resource type collection helper" do + Puppet::Parser::Compiler.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) + end + + it "should be able to return a class list containing all added classes" do + @compiler.add_class "" + @compiler.add_class "one" + @compiler.add_class "two" + + @compiler.classlist.sort.should == %w{one two}.sort + end + + describe "when initializing" do + + it "should set its node attribute" do + @compiler.node.should equal(@node) + end + it "should detect when ast nodes are absent" do + @compiler.ast_nodes?.should be_false + end + + it "should detect when ast nodes are present" do + @known_resource_types.expects(:nodes?).returns true + @compiler.ast_nodes?.should be_true + end + + it "should copy the known_resource_types version to the catalog" do + @compiler.catalog.version.should == @known_resource_types.version + end + + it "should copy any node classes into the class list" do + node = Puppet::Node.new("mynode") + node.classes = %w{foo bar} + compiler = Puppet::Parser::Compiler.new(node) + + compiler.classlist.should include("foo") + compiler.classlist.should include("bar") + end + + it "should add a 'main' stage to the catalog" do + @compiler.catalog.resource(:stage, :main).should be_instance_of(Puppet::Parser::Resource) + end + end + + describe "when managing scopes" do + + it "should create a top scope" do + @compiler.topscope.should be_instance_of(Puppet::Parser::Scope) + end + + it "should be able to create new scopes" do + @compiler.newscope(@compiler.topscope).should be_instance_of(Puppet::Parser::Scope) + end + + it "should correctly set the level of newly created scopes" do + @compiler.newscope(@compiler.topscope, :level => 5).level.should == 5 + end + + it "should set the parent scope of the new scope to be the passed-in parent" do + scope = mock 'scope' + newscope = @compiler.newscope(scope) + + newscope.parent.should equal(scope) + end + + it "should set the parent scope of the new scope to its topscope if the parent passed in is nil" do + scope = mock 'scope' + newscope = @compiler.newscope(nil) + + newscope.parent.should equal(@compiler.topscope) + end + end + + describe "when compiling" do + + def compile_methods + [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, + :finish, :store, :extract, :evaluate_relationships] + end + + # Stub all of the main compile methods except the ones we're specifically interested in. + def compile_stub(*except) + (compile_methods - except).each { |m| @compiler.stubs(m) } + end + + it "should set node parameters as variables in the top scope" do + params = {"a" => "b", "c" => "d"} + @node.stubs(:parameters).returns(params) + compile_stub(:set_node_parameters) + @compiler.compile + @compiler.topscope.lookupvar("a").should == "b" + @compiler.topscope.lookupvar("c").should == "d" + end + + it "should set the client and server versions on the catalog" do + params = {"clientversion" => "2", "serverversion" => "3"} + @node.stubs(:parameters).returns(params) + compile_stub(:set_node_parameters) + @compiler.compile + @compiler.catalog.client_version.should == "2" + @compiler.catalog.server_version.should == "3" + end + + it "should evaluate any existing classes named in the node" do + classes = %w{one two three four} + main = stub 'main' + one = stub 'one', :name => "one" + three = stub 'three', :name => "three" + @node.stubs(:name).returns("whatever") + @node.stubs(:classes).returns(classes) + + @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) + @compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes } + end + + it "should evaluate the main class if it exists" do + compile_stub(:evaluate_main) + main_class = @known_resource_types.add Puppet::Resource::Type.new(:hostclass, "") + main_class.expects(:evaluate_code).with { |r| r.is_a?(Puppet::Parser::Resource) } + @compiler.topscope.expects(:source=).with(main_class) + + @compiler.compile + end + + it "should create a new, empty 'main' if no main class exists" do + compile_stub(:evaluate_main) + @compiler.compile + @known_resource_types.find_hostclass([""], "").should be_instance_of(Puppet::Resource::Type) + end + + it "should add an edge between the main stage and main class" do + @compiler.compile + (stage = @compiler.catalog.resource(:stage, "main")).should be_instance_of(Puppet::Parser::Resource) + (klass = @compiler.catalog.resource(:class, "")).should be_instance_of(Puppet::Parser::Resource) + + @compiler.catalog.edge?(stage, klass).should be_true + end + + it "should evaluate any node classes" do + @node.stubs(:classes).returns(%w{one two three four}) + @compiler.expects(:evaluate_classes).with(%w{one two three four}, @compiler.topscope) + @compiler.send(:evaluate_node_classes) + end + + it "should evaluate all added collections" do + colls = [] + # And when the collections fail to evaluate. + colls << mock("coll1-false") + colls << mock("coll2-false") + colls.each { |c| c.expects(:evaluate).returns(false) } + + @compiler.add_collection(colls[0]) + @compiler.add_collection(colls[1]) + + compile_stub(:evaluate_generators) + @compiler.compile + end + + it "should ignore builtin resources" do + resource = resource(:file, "testing") + + @compiler.add_resource(@scope, resource) + resource.expects(:evaluate).never + + @compiler.compile + end + + it "should evaluate unevaluated resources" do + resource = CompilerTestResource.new(:file, "testing") + + @compiler.add_resource(@scope, resource) + + # We have to now mark the resource as evaluated + resource.expects(:evaluate).with { |*whatever| resource.evaluated = true } + + @compiler.compile + end + + it "should not evaluate already-evaluated resources" do + resource = resource(:file, "testing") + resource.stubs(:evaluated?).returns true + + @compiler.add_resource(@scope, resource) + resource.expects(:evaluate).never + + @compiler.compile + end + + it "should evaluate unevaluated resources created by evaluating other resources" do + resource = CompilerTestResource.new(:file, "testing") + @compiler.add_resource(@scope, resource) + + resource2 = CompilerTestResource.new(:file, "other") + + # We have to now mark the resource as evaluated + resource.expects(:evaluate).with { |*whatever| resource.evaluated = true; @compiler.add_resource(@scope, resource2) } + resource2.expects(:evaluate).with { |*whatever| resource2.evaluated = true } + + + @compiler.compile + end + + describe "when finishing" do + before do + @compiler.send(:evaluate_main) + @catalog = @compiler.catalog + end + + def add_resource(name, parent = nil) + resource = Puppet::Parser::Resource.new "file", name, :scope => @scope + @compiler.add_resource(@scope, resource) + @catalog.add_edge(parent, resource) if parent + resource + end + + it "should call finish() on all resources" do + # Add a resource that does respond to :finish + resource = Puppet::Parser::Resource.new "file", "finish", :scope => @scope + resource.expects(:finish) + + @compiler.add_resource(@scope, resource) + + # And one that does not + dnf_resource = stub_everything "dnf", :ref => "File[dnf]", :type => "file" + + @compiler.add_resource(@scope, dnf_resource) + + @compiler.send(:finish) + end + + it "should call finish() in add_resource order" do + resources = sequence('resources') + + resource1 = add_resource("finish1") + resource1.expects(:finish).in_sequence(resources) + + resource2 = add_resource("finish2") + resource2.expects(:finish).in_sequence(resources) + + @compiler.send(:finish) + end + + it "should add each container's metaparams to its contained resources" do + main = @catalog.resource(:class, :main) + main[:noop] = true + + resource1 = add_resource("meh", main) + + @compiler.send(:finish) + resource1[:noop].should be_true + end + + it "should add metaparams recursively" do + main = @catalog.resource(:class, :main) + main[:noop] = true + + resource1 = add_resource("meh", main) + resource2 = add_resource("foo", resource1) + + @compiler.send(:finish) + resource2[:noop].should be_true + end + + it "should prefer metaparams from immediate parents" do + main = @catalog.resource(:class, :main) + main[:noop] = true + + resource1 = add_resource("meh", main) + resource2 = add_resource("foo", resource1) + + resource1[:noop] = false + + @compiler.send(:finish) + resource2[:noop].should be_false + end + + it "should merge tags downward" do + main = @catalog.resource(:class, :main) + main.tag("one") + + resource1 = add_resource("meh", main) + resource1.tag "two" + resource2 = add_resource("foo", resource1) + + @compiler.send(:finish) + resource2.tags.should be_include("one") + resource2.tags.should be_include("two") + end + + it "should work if only middle resources have metaparams set" do + main = @catalog.resource(:class, :main) + + resource1 = add_resource("meh", main) + resource1[:noop] = true + resource2 = add_resource("foo", resource1) + + @compiler.send(:finish) + resource2[:noop].should be_true + end + end + + it "should return added resources in add order" do + resource1 = resource(:file, "yay") + @compiler.add_resource(@scope, resource1) + resource2 = resource(:file, "youpi") + @compiler.add_resource(@scope, resource2) + + @compiler.resources.should == [resource1, resource2] + end + + it "should add resources that do not conflict with existing resources" do + resource = resource(:file, "yay") + @compiler.add_resource(@scope, resource) + + @compiler.catalog.should be_vertex(resource) + end + + it "should fail to add resources that conflict with existing resources" do + path = Puppet.features.posix? ? "/foo" : "C:/foo" + file1 = Puppet::Type.type(:file).new :path => path + file2 = Puppet::Type.type(:file).new :path => path + + @compiler.add_resource(@scope, file1) + lambda { @compiler.add_resource(@scope, file2) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) + end + + it "should add an edge from the scope resource to the added resource" do + resource = resource(:file, "yay") + @compiler.add_resource(@scope, resource) + + @compiler.catalog.should be_edge(@scope.resource, resource) + end + + it "should add an edge to any specified stage for class resources" do + other_stage = resource(:stage, "other") + @compiler.add_resource(@scope, other_stage) + resource = resource(:class, "foo") + resource[:stage] = 'other' + + @compiler.add_resource(@scope, resource) + + @compiler.catalog.edge?(other_stage, resource).should be_true + end + + it "should fail if a non-class resource attempts to set a stage" do + other_stage = resource(:stage, "other") + @compiler.add_resource(@scope, other_stage) + resource = resource(:file, "foo") + resource[:stage] = 'other' + + lambda { @compiler.add_resource(@scope, resource) }.should raise_error(ArgumentError) + end + + it "should fail if an unknown stage is specified" do + resource = resource(:class, "foo") + resource[:stage] = 'other' + + lambda { @compiler.add_resource(@scope, resource) }.should raise_error(ArgumentError) + end + + it "should add edges from the class resources to the main stage if no stage is specified" do + main = @compiler.catalog.resource(:stage, :main) + resource = resource(:class, "foo") + @compiler.add_resource(@scope, resource) + + @compiler.catalog.should be_edge(main, resource) + end + + it "should not add non-class resources that don't specify a stage to the 'main' stage" do + main = @compiler.catalog.resource(:stage, :main) + resource = resource(:file, "foo") + @compiler.add_resource(@scope, resource) + + @compiler.catalog.should_not be_edge(main, resource) + end + + it "should not add any parent-edges to stages" do + stage = resource(:stage, "other") + @compiler.add_resource(@scope, stage) + + @scope.resource = resource(:class, "foo") + + @compiler.catalog.edge?(@scope.resource, stage).should be_false + end + + it "should not attempt to add stages to other stages" do + other_stage = resource(:stage, "other") + second_stage = resource(:stage, "second") + @compiler.add_resource(@scope, other_stage) + @compiler.add_resource(@scope, second_stage) + + second_stage[:stage] = "other" + + @compiler.catalog.edge?(other_stage, second_stage).should be_false + end + + it "should have a method for looking up resources" do + resource = resource(:yay, "foo") + @compiler.add_resource(@scope, resource) + @compiler.findresource("Yay[foo]").should equal(resource) + end + + it "should be able to look resources up by type and title" do + resource = resource(:yay, "foo") + @compiler.add_resource(@scope, resource) + @compiler.findresource("Yay", "foo").should equal(resource) + end + + it "should not evaluate virtual defined resources" do + resource = resource(:file, "testing") + resource.virtual = true + @compiler.add_resource(@scope, resource) + + resource.expects(:evaluate).never + + @compiler.compile + end + end + + describe "when evaluating collections" do + + it "should evaluate each collection" do + 2.times { |i| + coll = mock 'coll%s' % i + @compiler.add_collection(coll) + + # This is the hard part -- we have to emulate the fact that + # collections delete themselves if they are done evaluating. + coll.expects(:evaluate).with do + @compiler.delete_collection(coll) + end + } + + @compiler.class.publicize_methods(:evaluate_collections) { @compiler.evaluate_collections } + end + + it "should not fail when there are unevaluated resource collections that do not refer to specific resources" do + coll = stub 'coll', :evaluate => false + coll.expects(:resources).returns(nil) + + @compiler.add_collection(coll) + + lambda { @compiler.compile }.should_not raise_error + end + + it "should fail when there are unevaluated resource collections that refer to a specific resource" do + coll = stub 'coll', :evaluate => false + coll.expects(:resources).returns(:something) + + @compiler.add_collection(coll) + + lambda { @compiler.compile }.should raise_error(Puppet::ParseError) + end + + it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do + coll = stub 'coll', :evaluate => false + coll.expects(:resources).returns([:one, :two]) + + @compiler.add_collection(coll) + + lambda { @compiler.compile }.should raise_error(Puppet::ParseError) + end + end + + describe "when evaluating relationships" do + it "should evaluate each relationship with its catalog" do + dep = stub 'dep' + dep.expects(:evaluate).with(@compiler.catalog) + @compiler.add_relationship dep + @compiler.evaluate_relationships + end + end + + describe "when told to evaluate missing classes" do + + it "should fail if there's no source listed for the scope" do + scope = stub 'scope', :source => nil + proc { @compiler.evaluate_classes(%w{one two}, scope) }.should raise_error(Puppet::DevError) + end + + it "should tag the catalog with the name of each not-found class" do + @compiler.catalog.expects(:tag).with("notfound") + @scope.expects(:find_hostclass).with("notfound").returns(nil) + @compiler.evaluate_classes(%w{notfound}, @scope) + end + end + + describe "when evaluating found classes" do + + before do + @class = stub 'class', :name => "my::class" + @scope.stubs(:find_hostclass).with("myclass").returns(@class) + + @resource = stub 'resource', :ref => "Class[myclass]", :type => "file" + end + + it "should evaluate each class" do + @compiler.catalog.stubs(:tag) + + @class.expects(:mk_plain_resource).with(@scope) + @scope.stubs(:class_scope).with(@class) + + @compiler.evaluate_classes(%w{myclass}, @scope) + end + + it "should not evaluate the resources created for found classes unless asked" do + @compiler.catalog.stubs(:tag) + + @resource.expects(:evaluate).never + + @class.expects(:mk_plain_resource).returns(@resource) + @scope.stubs(:class_scope).with(@class) + + @compiler.evaluate_classes(%w{myclass}, @scope) + end + + it "should immediately evaluate the resources created for found classes when asked" do + @compiler.catalog.stubs(:tag) + + @resource.expects(:evaluate) + @class.expects(:mk_plain_resource).returns(@resource) + @scope.stubs(:class_scope).with(@class) + + @compiler.evaluate_classes(%w{myclass}, @scope, false) + end + + it "should skip classes that have already been evaluated" do + @compiler.catalog.stubs(:tag) + + @scope.stubs(:class_scope).with(@class).returns("something") + + @compiler.expects(:add_resource).never + + @resource.expects(:evaluate).never + + Puppet::Parser::Resource.expects(:new).never + @compiler.evaluate_classes(%w{myclass}, @scope, false) + end + + it "should skip classes previously evaluated with different capitalization" do + @compiler.catalog.stubs(:tag) + @scope.stubs(:find_hostclass).with("MyClass").returns(@class) + @scope.stubs(:class_scope).with(@class).returns("something") + @compiler.expects(:add_resource).never + @resource.expects(:evaluate).never + Puppet::Parser::Resource.expects(:new).never + @compiler.evaluate_classes(%w{MyClass}, @scope, false) + end + + it "should return the list of found classes" do + @compiler.catalog.stubs(:tag) + + @compiler.stubs(:add_resource) + @scope.stubs(:find_hostclass).with("notfound").returns(nil) + @scope.stubs(:class_scope).with(@class) + + Puppet::Parser::Resource.stubs(:new).returns(@resource) + @class.stubs :mk_plain_resource + @compiler.evaluate_classes(%w{myclass notfound}, @scope).should == %w{myclass} + end + end + + describe "when evaluating AST nodes with no AST nodes present" do + + it "should do nothing" do + @compiler.expects(:ast_nodes?).returns(false) + @compiler.known_resource_types.expects(:nodes).never + Puppet::Parser::Resource.expects(:new).never + + @compiler.send(:evaluate_ast_node) + end + end + + describe "when evaluating AST nodes with AST nodes present" do + + before do + @compiler.known_resource_types.stubs(:nodes?).returns true + + # Set some names for our test + @node.stubs(:names).returns(%w{a b c}) + @compiler.known_resource_types.stubs(:node).with("a").returns(nil) + @compiler.known_resource_types.stubs(:node).with("b").returns(nil) + @compiler.known_resource_types.stubs(:node).with("c").returns(nil) + + # It should check this last, of course. + @compiler.known_resource_types.stubs(:node).with("default").returns(nil) + end + + it "should fail if the named node cannot be found" do + proc { @compiler.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError) + end + + it "should evaluate the first node class matching the node name" do + node_class = stub 'node', :name => "c", :evaluate_code => nil + @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" + node_class.expects(:mk_plain_resource).returns(node_resource) + + @compiler.compile + end + + it "should match the default node if no matching node can be found" do + node_class = stub 'node', :name => "default", :evaluate_code => nil + @compiler.known_resource_types.stubs(:node).with("default").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil, :type => "node" + node_class.expects(:mk_plain_resource).returns(node_resource) + + @compiler.compile + end + + it "should evaluate the node resource immediately rather than using lazy evaluation" do + node_class = stub 'node', :name => "c" + @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[c]", :type => "node" + node_class.expects(:mk_plain_resource).returns(node_resource) + + node_resource.expects(:evaluate) + + @compiler.send(:evaluate_ast_node) + end + + it "should set the node's scope as the top scope" do + node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" + node_class = stub 'node', :name => "c", :mk_plain_resource => node_resource + + @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) + + # The #evaluate method normally does this. + scope = stub 'scope', :source => "mysource" + @compiler.topscope.expects(:class_scope).with(node_class).returns(scope) + node_resource.stubs(:evaluate) + + @compiler.compile + + @compiler.topscope.should equal(scope) + end + end + + describe "when managing resource overrides" do + + before do + @override = stub 'override', :ref => "File[/foo]", :type => "my" + @resource = resource(:file, "/foo") + end + + it "should be able to store overrides" do + lambda { @compiler.add_override(@override) }.should_not raise_error + end + + it "should apply overrides to the appropriate resources" do + @compiler.add_resource(@scope, @resource) + @resource.expects(:merge).with(@override) + + @compiler.add_override(@override) + + @compiler.compile + end + + it "should accept overrides before the related resource has been created" do + @resource.expects(:merge).with(@override) + + # First store the override + @compiler.add_override(@override) + + # Then the resource + @compiler.add_resource(@scope, @resource) + + # And compile, so they get resolved + @compiler.compile + end + + it "should fail if the compile is finished and resource overrides have not been applied" do + @compiler.add_override(@override) + + lambda { @compiler.compile }.should raise_error(Puppet::ParseError) + end + end +end diff --git a/spec/unit/parser/compiler_spec_spec.rb b/spec/unit/parser/compiler_spec_spec.rb deleted file mode 100755 index 67bfc3749..000000000 --- a/spec/unit/parser/compiler_spec_spec.rb +++ /dev/null @@ -1,753 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -class CompilerTestResource - attr_accessor :builtin, :virtual, :evaluated, :type, :title - - def initialize(type, title) - @type = type - @title = title - end - - def [](attr) - return nil if attr == :stage - :main - end - - def ref - "%s[%s]" % [type.to_s.capitalize, title] - end - - def evaluated? - @evaluated - end - - def builtin? - @builtin - end - - def virtual? - @virtual - end - - def evaluate - end -end - -describe Puppet::Parser::Compiler do - def resource(type, title) - Puppet::Parser::Resource.new(type, title, :scope => @scope) - end - - before :each do - @node = Puppet::Node.new "testnode" - @known_resource_types = Puppet::Resource::TypeCollection.new "development" - @compiler = Puppet::Parser::Compiler.new(@node) - @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => stub('source')) - @scope_resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope) - @scope.resource = @scope_resource - @compiler.environment.stubs(:known_resource_types).returns @known_resource_types - end - - it "should have a class method that compiles, converts, and returns a catalog" do - compiler = stub 'compiler' - Puppet::Parser::Compiler.expects(:new).with(@node).returns compiler - catalog = stub 'catalog' - compiler.expects(:compile).returns catalog - converted_catalog = stub 'converted_catalog' - catalog.expects(:to_resource).returns converted_catalog - - Puppet::Parser::Compiler.compile(@node).should equal(converted_catalog) - end - - it "should fail intelligently when a class-level compile fails" do - Puppet::Parser::Compiler.expects(:new).raises ArgumentError - lambda { Puppet::Parser::Compiler.compile(@node) }.should raise_error(Puppet::Error) - end - - it "should use the node's environment as its environment" do - @compiler.environment.should equal(@node.environment) - end - - it "should include the resource type collection helper" do - Puppet::Parser::Compiler.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) - end - - it "should be able to return a class list containing all added classes" do - @compiler.add_class "" - @compiler.add_class "one" - @compiler.add_class "two" - - @compiler.classlist.sort.should == %w{one two}.sort - end - - describe "when initializing" do - - it "should set its node attribute" do - @compiler.node.should equal(@node) - end - it "should detect when ast nodes are absent" do - @compiler.ast_nodes?.should be_false - end - - it "should detect when ast nodes are present" do - @known_resource_types.expects(:nodes?).returns true - @compiler.ast_nodes?.should be_true - end - - it "should copy the known_resource_types version to the catalog" do - @compiler.catalog.version.should == @known_resource_types.version - end - - it "should copy any node classes into the class list" do - node = Puppet::Node.new("mynode") - node.classes = %w{foo bar} - compiler = Puppet::Parser::Compiler.new(node) - - compiler.classlist.should include("foo") - compiler.classlist.should include("bar") - end - - it "should add a 'main' stage to the catalog" do - @compiler.catalog.resource(:stage, :main).should be_instance_of(Puppet::Parser::Resource) - end - end - - describe "when managing scopes" do - - it "should create a top scope" do - @compiler.topscope.should be_instance_of(Puppet::Parser::Scope) - end - - it "should be able to create new scopes" do - @compiler.newscope(@compiler.topscope).should be_instance_of(Puppet::Parser::Scope) - end - - it "should correctly set the level of newly created scopes" do - @compiler.newscope(@compiler.topscope, :level => 5).level.should == 5 - end - - it "should set the parent scope of the new scope to be the passed-in parent" do - scope = mock 'scope' - newscope = @compiler.newscope(scope) - - newscope.parent.should equal(scope) - end - - it "should set the parent scope of the new scope to its topscope if the parent passed in is nil" do - scope = mock 'scope' - newscope = @compiler.newscope(nil) - - newscope.parent.should equal(@compiler.topscope) - end - end - - describe "when compiling" do - - def compile_methods - [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, - :finish, :store, :extract, :evaluate_relationships] - end - - # Stub all of the main compile methods except the ones we're specifically interested in. - def compile_stub(*except) - (compile_methods - except).each { |m| @compiler.stubs(m) } - end - - it "should set node parameters as variables in the top scope" do - params = {"a" => "b", "c" => "d"} - @node.stubs(:parameters).returns(params) - compile_stub(:set_node_parameters) - @compiler.compile - @compiler.topscope.lookupvar("a").should == "b" - @compiler.topscope.lookupvar("c").should == "d" - end - - it "should set the client and server versions on the catalog" do - params = {"clientversion" => "2", "serverversion" => "3"} - @node.stubs(:parameters).returns(params) - compile_stub(:set_node_parameters) - @compiler.compile - @compiler.catalog.client_version.should == "2" - @compiler.catalog.server_version.should == "3" - end - - it "should evaluate any existing classes named in the node" do - classes = %w{one two three four} - main = stub 'main' - one = stub 'one', :name => "one" - three = stub 'three', :name => "three" - @node.stubs(:name).returns("whatever") - @node.stubs(:classes).returns(classes) - - @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) - @compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes } - end - - it "should evaluate the main class if it exists" do - compile_stub(:evaluate_main) - main_class = @known_resource_types.add Puppet::Resource::Type.new(:hostclass, "") - main_class.expects(:evaluate_code).with { |r| r.is_a?(Puppet::Parser::Resource) } - @compiler.topscope.expects(:source=).with(main_class) - - @compiler.compile - end - - it "should create a new, empty 'main' if no main class exists" do - compile_stub(:evaluate_main) - @compiler.compile - @known_resource_types.find_hostclass([""], "").should be_instance_of(Puppet::Resource::Type) - end - - it "should add an edge between the main stage and main class" do - @compiler.compile - (stage = @compiler.catalog.resource(:stage, "main")).should be_instance_of(Puppet::Parser::Resource) - (klass = @compiler.catalog.resource(:class, "")).should be_instance_of(Puppet::Parser::Resource) - - @compiler.catalog.edge?(stage, klass).should be_true - end - - it "should evaluate any node classes" do - @node.stubs(:classes).returns(%w{one two three four}) - @compiler.expects(:evaluate_classes).with(%w{one two three four}, @compiler.topscope) - @compiler.send(:evaluate_node_classes) - end - - it "should evaluate all added collections" do - colls = [] - # And when the collections fail to evaluate. - colls << mock("coll1-false") - colls << mock("coll2-false") - colls.each { |c| c.expects(:evaluate).returns(false) } - - @compiler.add_collection(colls[0]) - @compiler.add_collection(colls[1]) - - compile_stub(:evaluate_generators) - @compiler.compile - end - - it "should ignore builtin resources" do - resource = resource(:file, "testing") - - @compiler.add_resource(@scope, resource) - resource.expects(:evaluate).never - - @compiler.compile - end - - it "should evaluate unevaluated resources" do - resource = CompilerTestResource.new(:file, "testing") - - @compiler.add_resource(@scope, resource) - - # We have to now mark the resource as evaluated - resource.expects(:evaluate).with { |*whatever| resource.evaluated = true } - - @compiler.compile - end - - it "should not evaluate already-evaluated resources" do - resource = resource(:file, "testing") - resource.stubs(:evaluated?).returns true - - @compiler.add_resource(@scope, resource) - resource.expects(:evaluate).never - - @compiler.compile - end - - it "should evaluate unevaluated resources created by evaluating other resources" do - resource = CompilerTestResource.new(:file, "testing") - @compiler.add_resource(@scope, resource) - - resource2 = CompilerTestResource.new(:file, "other") - - # We have to now mark the resource as evaluated - resource.expects(:evaluate).with { |*whatever| resource.evaluated = true; @compiler.add_resource(@scope, resource2) } - resource2.expects(:evaluate).with { |*whatever| resource2.evaluated = true } - - - @compiler.compile - end - - describe "when finishing" do - before do - @compiler.send(:evaluate_main) - @catalog = @compiler.catalog - end - - def add_resource(name, parent = nil) - resource = Puppet::Parser::Resource.new "file", name, :scope => @scope - @compiler.add_resource(@scope, resource) - @catalog.add_edge(parent, resource) if parent - resource - end - - it "should call finish() on all resources" do - # Add a resource that does respond to :finish - resource = Puppet::Parser::Resource.new "file", "finish", :scope => @scope - resource.expects(:finish) - - @compiler.add_resource(@scope, resource) - - # And one that does not - dnf_resource = stub_everything "dnf", :ref => "File[dnf]", :type => "file" - - @compiler.add_resource(@scope, dnf_resource) - - @compiler.send(:finish) - end - - it "should call finish() in add_resource order" do - resources = sequence('resources') - - resource1 = add_resource("finish1") - resource1.expects(:finish).in_sequence(resources) - - resource2 = add_resource("finish2") - resource2.expects(:finish).in_sequence(resources) - - @compiler.send(:finish) - end - - it "should add each container's metaparams to its contained resources" do - main = @catalog.resource(:class, :main) - main[:noop] = true - - resource1 = add_resource("meh", main) - - @compiler.send(:finish) - resource1[:noop].should be_true - end - - it "should add metaparams recursively" do - main = @catalog.resource(:class, :main) - main[:noop] = true - - resource1 = add_resource("meh", main) - resource2 = add_resource("foo", resource1) - - @compiler.send(:finish) - resource2[:noop].should be_true - end - - it "should prefer metaparams from immediate parents" do - main = @catalog.resource(:class, :main) - main[:noop] = true - - resource1 = add_resource("meh", main) - resource2 = add_resource("foo", resource1) - - resource1[:noop] = false - - @compiler.send(:finish) - resource2[:noop].should be_false - end - - it "should merge tags downward" do - main = @catalog.resource(:class, :main) - main.tag("one") - - resource1 = add_resource("meh", main) - resource1.tag "two" - resource2 = add_resource("foo", resource1) - - @compiler.send(:finish) - resource2.tags.should be_include("one") - resource2.tags.should be_include("two") - end - - it "should work if only middle resources have metaparams set" do - main = @catalog.resource(:class, :main) - - resource1 = add_resource("meh", main) - resource1[:noop] = true - resource2 = add_resource("foo", resource1) - - @compiler.send(:finish) - resource2[:noop].should be_true - end - end - - it "should return added resources in add order" do - resource1 = resource(:file, "yay") - @compiler.add_resource(@scope, resource1) - resource2 = resource(:file, "youpi") - @compiler.add_resource(@scope, resource2) - - @compiler.resources.should == [resource1, resource2] - end - - it "should add resources that do not conflict with existing resources" do - resource = resource(:file, "yay") - @compiler.add_resource(@scope, resource) - - @compiler.catalog.should be_vertex(resource) - end - - it "should fail to add resources that conflict with existing resources" do - path = Puppet.features.posix? ? "/foo" : "C:/foo" - file1 = Puppet::Type.type(:file).new :path => path - file2 = Puppet::Type.type(:file).new :path => path - - @compiler.add_resource(@scope, file1) - lambda { @compiler.add_resource(@scope, file2) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) - end - - it "should add an edge from the scope resource to the added resource" do - resource = resource(:file, "yay") - @compiler.add_resource(@scope, resource) - - @compiler.catalog.should be_edge(@scope.resource, resource) - end - - it "should add an edge to any specified stage for class resources" do - other_stage = resource(:stage, "other") - @compiler.add_resource(@scope, other_stage) - resource = resource(:class, "foo") - resource[:stage] = 'other' - - @compiler.add_resource(@scope, resource) - - @compiler.catalog.edge?(other_stage, resource).should be_true - end - - it "should fail if a non-class resource attempts to set a stage" do - other_stage = resource(:stage, "other") - @compiler.add_resource(@scope, other_stage) - resource = resource(:file, "foo") - resource[:stage] = 'other' - - lambda { @compiler.add_resource(@scope, resource) }.should raise_error(ArgumentError) - end - - it "should fail if an unknown stage is specified" do - resource = resource(:class, "foo") - resource[:stage] = 'other' - - lambda { @compiler.add_resource(@scope, resource) }.should raise_error(ArgumentError) - end - - it "should add edges from the class resources to the main stage if no stage is specified" do - main = @compiler.catalog.resource(:stage, :main) - resource = resource(:class, "foo") - @compiler.add_resource(@scope, resource) - - @compiler.catalog.should be_edge(main, resource) - end - - it "should not add non-class resources that don't specify a stage to the 'main' stage" do - main = @compiler.catalog.resource(:stage, :main) - resource = resource(:file, "foo") - @compiler.add_resource(@scope, resource) - - @compiler.catalog.should_not be_edge(main, resource) - end - - it "should not add any parent-edges to stages" do - stage = resource(:stage, "other") - @compiler.add_resource(@scope, stage) - - @scope.resource = resource(:class, "foo") - - @compiler.catalog.edge?(@scope.resource, stage).should be_false - end - - it "should not attempt to add stages to other stages" do - other_stage = resource(:stage, "other") - second_stage = resource(:stage, "second") - @compiler.add_resource(@scope, other_stage) - @compiler.add_resource(@scope, second_stage) - - second_stage[:stage] = "other" - - @compiler.catalog.edge?(other_stage, second_stage).should be_false - end - - it "should have a method for looking up resources" do - resource = resource(:yay, "foo") - @compiler.add_resource(@scope, resource) - @compiler.findresource("Yay[foo]").should equal(resource) - end - - it "should be able to look resources up by type and title" do - resource = resource(:yay, "foo") - @compiler.add_resource(@scope, resource) - @compiler.findresource("Yay", "foo").should equal(resource) - end - - it "should not evaluate virtual defined resources" do - resource = resource(:file, "testing") - resource.virtual = true - @compiler.add_resource(@scope, resource) - - resource.expects(:evaluate).never - - @compiler.compile - end - end - - describe "when evaluating collections" do - - it "should evaluate each collection" do - 2.times { |i| - coll = mock 'coll%s' % i - @compiler.add_collection(coll) - - # This is the hard part -- we have to emulate the fact that - # collections delete themselves if they are done evaluating. - coll.expects(:evaluate).with do - @compiler.delete_collection(coll) - end - } - - @compiler.class.publicize_methods(:evaluate_collections) { @compiler.evaluate_collections } - end - - it "should not fail when there are unevaluated resource collections that do not refer to specific resources" do - coll = stub 'coll', :evaluate => false - coll.expects(:resources).returns(nil) - - @compiler.add_collection(coll) - - lambda { @compiler.compile }.should_not raise_error - end - - it "should fail when there are unevaluated resource collections that refer to a specific resource" do - coll = stub 'coll', :evaluate => false - coll.expects(:resources).returns(:something) - - @compiler.add_collection(coll) - - lambda { @compiler.compile }.should raise_error(Puppet::ParseError) - end - - it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do - coll = stub 'coll', :evaluate => false - coll.expects(:resources).returns([:one, :two]) - - @compiler.add_collection(coll) - - lambda { @compiler.compile }.should raise_error(Puppet::ParseError) - end - end - - describe "when evaluating relationships" do - it "should evaluate each relationship with its catalog" do - dep = stub 'dep' - dep.expects(:evaluate).with(@compiler.catalog) - @compiler.add_relationship dep - @compiler.evaluate_relationships - end - end - - describe "when told to evaluate missing classes" do - - it "should fail if there's no source listed for the scope" do - scope = stub 'scope', :source => nil - proc { @compiler.evaluate_classes(%w{one two}, scope) }.should raise_error(Puppet::DevError) - end - - it "should tag the catalog with the name of each not-found class" do - @compiler.catalog.expects(:tag).with("notfound") - @scope.expects(:find_hostclass).with("notfound").returns(nil) - @compiler.evaluate_classes(%w{notfound}, @scope) - end - end - - describe "when evaluating found classes" do - - before do - @class = stub 'class', :name => "my::class" - @scope.stubs(:find_hostclass).with("myclass").returns(@class) - - @resource = stub 'resource', :ref => "Class[myclass]", :type => "file" - end - - it "should evaluate each class" do - @compiler.catalog.stubs(:tag) - - @class.expects(:mk_plain_resource).with(@scope) - @scope.stubs(:class_scope).with(@class) - - @compiler.evaluate_classes(%w{myclass}, @scope) - end - - it "should not evaluate the resources created for found classes unless asked" do - @compiler.catalog.stubs(:tag) - - @resource.expects(:evaluate).never - - @class.expects(:mk_plain_resource).returns(@resource) - @scope.stubs(:class_scope).with(@class) - - @compiler.evaluate_classes(%w{myclass}, @scope) - end - - it "should immediately evaluate the resources created for found classes when asked" do - @compiler.catalog.stubs(:tag) - - @resource.expects(:evaluate) - @class.expects(:mk_plain_resource).returns(@resource) - @scope.stubs(:class_scope).with(@class) - - @compiler.evaluate_classes(%w{myclass}, @scope, false) - end - - it "should skip classes that have already been evaluated" do - @compiler.catalog.stubs(:tag) - - @scope.stubs(:class_scope).with(@class).returns("something") - - @compiler.expects(:add_resource).never - - @resource.expects(:evaluate).never - - Puppet::Parser::Resource.expects(:new).never - @compiler.evaluate_classes(%w{myclass}, @scope, false) - end - - it "should skip classes previously evaluated with different capitalization" do - @compiler.catalog.stubs(:tag) - @scope.stubs(:find_hostclass).with("MyClass").returns(@class) - @scope.stubs(:class_scope).with(@class).returns("something") - @compiler.expects(:add_resource).never - @resource.expects(:evaluate).never - Puppet::Parser::Resource.expects(:new).never - @compiler.evaluate_classes(%w{MyClass}, @scope, false) - end - - it "should return the list of found classes" do - @compiler.catalog.stubs(:tag) - - @compiler.stubs(:add_resource) - @scope.stubs(:find_hostclass).with("notfound").returns(nil) - @scope.stubs(:class_scope).with(@class) - - Puppet::Parser::Resource.stubs(:new).returns(@resource) - @class.stubs :mk_plain_resource - @compiler.evaluate_classes(%w{myclass notfound}, @scope).should == %w{myclass} - end - end - - describe "when evaluating AST nodes with no AST nodes present" do - - it "should do nothing" do - @compiler.expects(:ast_nodes?).returns(false) - @compiler.known_resource_types.expects(:nodes).never - Puppet::Parser::Resource.expects(:new).never - - @compiler.send(:evaluate_ast_node) - end - end - - describe "when evaluating AST nodes with AST nodes present" do - - before do - @compiler.known_resource_types.stubs(:nodes?).returns true - - # Set some names for our test - @node.stubs(:names).returns(%w{a b c}) - @compiler.known_resource_types.stubs(:node).with("a").returns(nil) - @compiler.known_resource_types.stubs(:node).with("b").returns(nil) - @compiler.known_resource_types.stubs(:node).with("c").returns(nil) - - # It should check this last, of course. - @compiler.known_resource_types.stubs(:node).with("default").returns(nil) - end - - it "should fail if the named node cannot be found" do - proc { @compiler.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError) - end - - it "should evaluate the first node class matching the node name" do - node_class = stub 'node', :name => "c", :evaluate_code => nil - @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) - - node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" - node_class.expects(:mk_plain_resource).returns(node_resource) - - @compiler.compile - end - - it "should match the default node if no matching node can be found" do - node_class = stub 'node', :name => "default", :evaluate_code => nil - @compiler.known_resource_types.stubs(:node).with("default").returns(node_class) - - node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil, :type => "node" - node_class.expects(:mk_plain_resource).returns(node_resource) - - @compiler.compile - end - - it "should evaluate the node resource immediately rather than using lazy evaluation" do - node_class = stub 'node', :name => "c" - @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) - - node_resource = stub 'node resource', :ref => "Node[c]", :type => "node" - node_class.expects(:mk_plain_resource).returns(node_resource) - - node_resource.expects(:evaluate) - - @compiler.send(:evaluate_ast_node) - end - - it "should set the node's scope as the top scope" do - node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" - node_class = stub 'node', :name => "c", :mk_plain_resource => node_resource - - @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) - - # The #evaluate method normally does this. - scope = stub 'scope', :source => "mysource" - @compiler.topscope.expects(:class_scope).with(node_class).returns(scope) - node_resource.stubs(:evaluate) - - @compiler.compile - - @compiler.topscope.should equal(scope) - end - end - - describe "when managing resource overrides" do - - before do - @override = stub 'override', :ref => "File[/foo]", :type => "my" - @resource = resource(:file, "/foo") - end - - it "should be able to store overrides" do - lambda { @compiler.add_override(@override) }.should_not raise_error - end - - it "should apply overrides to the appropriate resources" do - @compiler.add_resource(@scope, @resource) - @resource.expects(:merge).with(@override) - - @compiler.add_override(@override) - - @compiler.compile - end - - it "should accept overrides before the related resource has been created" do - @resource.expects(:merge).with(@override) - - # First store the override - @compiler.add_override(@override) - - # Then the resource - @compiler.add_resource(@scope, @resource) - - # And compile, so they get resolved - @compiler.compile - end - - it "should fail if the compile is finished and resource overrides have not been applied" do - @compiler.add_override(@override) - - lambda { @compiler.compile }.should raise_error(Puppet::ParseError) - end - end -end diff --git a/spec/unit/parser/files_spec.rb b/spec/unit/parser/files_spec.rb new file mode 100644 index 000000000..89215344b --- /dev/null +++ b/spec/unit/parser/files_spec.rb @@ -0,0 +1,195 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/parser/files' + +describe Puppet::Parser::Files do + + before do + @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" + end + + it "should have a method for finding a template" do + Puppet::Parser::Files.should respond_to(:find_template) + end + + it "should have a method for finding manifests" do + Puppet::Parser::Files.should respond_to(:find_manifests) + end + + describe "when searching for templates" do + it "should return fully-qualified templates directly" do + Puppet::Parser::Files.expects(:modulepath).never + Puppet::Parser::Files.find_template(@basepath + "/my/template").should == @basepath + "/my/template" + end + + it "should return the template from the first found module" do + mod = mock 'module' + Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod + + mod.expects(:template).returns("/one/mymod/templates/mytemplate") + Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" + end + + it "should return the file in the templatedir if it exists" do + Puppet.settings.expects(:value).with(:templatedir, nil).returns("/my/templates") + Puppet[:modulepath] = "/one:/two" + File.stubs(:directory?).returns(true) + FileTest.stubs(:exist?).returns(true) + Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/my/templates/mymod/mytemplate" + end + + it "should not raise an error if no valid templatedir exists and the template exists in a module" do + mod = mock 'module' + Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod + + mod.expects(:template).returns("/one/mymod/templates/mytemplate") + Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(nil) + + Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" + end + + it "should return unqualified templates if they exist in the template dir" do + FileTest.stubs(:exist?).returns true + Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) + Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" + end + + it "should only return templates if they actually exist" do + FileTest.expects(:exist?).with("/my/templates/mytemplate").returns true + Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) + Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" + end + + it "should return nil when asked for a template that doesn't exist" do + FileTest.expects(:exist?).with("/my/templates/mytemplate").returns false + Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) + Puppet::Parser::Files.find_template("mytemplate").should be_nil + end + + it "should search in the template directories before modules" do + FileTest.stubs(:exist?).returns true + Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) + Puppet::Module.expects(:find).never + Puppet::Parser::Files.find_template("mytemplate") + end + + it "should accept relative templatedirs" do + FileTest.stubs(:exist?).returns true + Puppet[:templatedir] = "my/templates" + File.expects(:directory?).with(File.join(Dir.getwd,"my/templates")).returns(true) + Puppet::Parser::Files.find_template("mytemplate").should == File.join(Dir.getwd,"my/templates/mytemplate") + end + + it "should use the environment templatedir if no module is found and an environment is specified" do + FileTest.stubs(:exist?).returns true + Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates"]) + Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" + end + + it "should use first dir from environment templatedir if no module is found and an environment is specified" do + FileTest.stubs(:exist?).returns true + Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates", "/two/templates"]) + Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" + end + + it "should use a valid dir when templatedir is a path for unqualified templates and the first dir contains template" do + Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) + FileTest.expects(:exist?).with("/one/templates/mytemplate").returns(true) + Puppet::Parser::Files.find_template("mytemplate").should == "/one/templates/mytemplate" + end + + it "should use a valid dir when templatedir is a path for unqualified templates and only second dir contains template" do + Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) + FileTest.expects(:exist?).with("/one/templates/mytemplate").returns(false) + FileTest.expects(:exist?).with("/two/templates/mytemplate").returns(true) + Puppet::Parser::Files.find_template("mytemplate").should == "/two/templates/mytemplate" + end + + it "should use the node environment if specified" do + mod = mock 'module' + Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod + + mod.expects(:template).returns("/my/modules/mymod/templates/envtemplate") + + Puppet::Parser::Files.find_template("mymod/envtemplate", "myenv").should == "/my/modules/mymod/templates/envtemplate" + end + + it "should return nil if no template can be found" do + Puppet::Parser::Files.find_template("foomod/envtemplate", "myenv").should be_nil + end + + after { Puppet.settings.clear } + end + + describe "when searching for manifests" do + it "should ignore invalid modules" do + mod = mock 'module' + Puppet::Node::Environment.new.expects(:module).with("mymod").raises(Puppet::Module::InvalidName, "name is invalid") + Puppet.expects(:value).with(:modulepath).never + Dir.stubs(:glob).returns %w{foo} + + Puppet::Parser::Files.find_manifests("mymod/init.pp") + end + end + + describe "when searching for manifests when no module is found" do + before do + File.stubs(:find).returns(nil) + end + + it "should not look for modules when paths are fully qualified" do + Puppet.expects(:value).with(:modulepath).never + file = @basepath + "/fully/qualified/file.pp" + Dir.stubs(:glob).with(file).returns([file]) + Puppet::Parser::Files.find_manifests(file) + end + + it "should return nil and an array of fully qualified files" do + file = @basepath + "/fully/qualified/file.pp" + Dir.stubs(:glob).with(file).returns([file]) + Puppet::Parser::Files.find_manifests(file).should == [nil, [file]] + end + + it "should match against provided fully qualified patterns" do + pattern = @basepath + "/fully/qualified/pattern/*" + Dir.expects(:glob).with(pattern).returns(%w{my file list}) + Puppet::Parser::Files.find_manifests(pattern)[1].should == %w{my file list} + end + + it "should look for files relative to the current directory" do + cwd = Dir.getwd + Dir.expects(:glob).with("#{cwd}/foobar/init.pp").returns(["#{cwd}/foobar/init.pp"]) + Puppet::Parser::Files.find_manifests("foobar/init.pp")[1].should == ["#{cwd}/foobar/init.pp"] + end + + it "should only return files, not directories" do + pattern = @basepath + "/fully/qualified/pattern/*" + file = @basepath + "/my/file" + dir = @basepath + "/my/directory" + Dir.expects(:glob).with(pattern).returns([file, dir]) + FileTest.expects(:directory?).with(file).returns(false) + FileTest.expects(:directory?).with(dir).returns(true) + Puppet::Parser::Files.find_manifests(pattern)[1].should == [file] + end + end + + describe "when searching for manifests in a found module" do + it "should return the name of the module and the manifests from the first found module" do + mod = Puppet::Module.new("mymod") + Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod + mod.expects(:match_manifests).with("init.pp").returns(%w{/one/mymod/manifests/init.pp}) + Puppet::Parser::Files.find_manifests("mymod/init.pp").should == ["mymod", ["/one/mymod/manifests/init.pp"]] + end + + it "should use the node environment if specified" do + mod = Puppet::Module.new("mymod") + Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod + mod.expects(:match_manifests).with("init.pp").returns(%w{/one/mymod/manifests/init.pp}) + Puppet::Parser::Files.find_manifests("mymod/init.pp", :environment => "myenv")[1].should == ["/one/mymod/manifests/init.pp"] + end + + after { Puppet.settings.clear } + end +end diff --git a/spec/unit/parser/files_spec_spec.rb b/spec/unit/parser/files_spec_spec.rb deleted file mode 100644 index 89215344b..000000000 --- a/spec/unit/parser/files_spec_spec.rb +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/parser/files' - -describe Puppet::Parser::Files do - - before do - @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" - end - - it "should have a method for finding a template" do - Puppet::Parser::Files.should respond_to(:find_template) - end - - it "should have a method for finding manifests" do - Puppet::Parser::Files.should respond_to(:find_manifests) - end - - describe "when searching for templates" do - it "should return fully-qualified templates directly" do - Puppet::Parser::Files.expects(:modulepath).never - Puppet::Parser::Files.find_template(@basepath + "/my/template").should == @basepath + "/my/template" - end - - it "should return the template from the first found module" do - mod = mock 'module' - Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod - - mod.expects(:template).returns("/one/mymod/templates/mytemplate") - Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" - end - - it "should return the file in the templatedir if it exists" do - Puppet.settings.expects(:value).with(:templatedir, nil).returns("/my/templates") - Puppet[:modulepath] = "/one:/two" - File.stubs(:directory?).returns(true) - FileTest.stubs(:exist?).returns(true) - Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/my/templates/mymod/mytemplate" - end - - it "should not raise an error if no valid templatedir exists and the template exists in a module" do - mod = mock 'module' - Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod - - mod.expects(:template).returns("/one/mymod/templates/mytemplate") - Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(nil) - - Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" - end - - it "should return unqualified templates if they exist in the template dir" do - FileTest.stubs(:exist?).returns true - Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) - Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" - end - - it "should only return templates if they actually exist" do - FileTest.expects(:exist?).with("/my/templates/mytemplate").returns true - Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) - Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" - end - - it "should return nil when asked for a template that doesn't exist" do - FileTest.expects(:exist?).with("/my/templates/mytemplate").returns false - Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) - Puppet::Parser::Files.find_template("mytemplate").should be_nil - end - - it "should search in the template directories before modules" do - FileTest.stubs(:exist?).returns true - Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) - Puppet::Module.expects(:find).never - Puppet::Parser::Files.find_template("mytemplate") - end - - it "should accept relative templatedirs" do - FileTest.stubs(:exist?).returns true - Puppet[:templatedir] = "my/templates" - File.expects(:directory?).with(File.join(Dir.getwd,"my/templates")).returns(true) - Puppet::Parser::Files.find_template("mytemplate").should == File.join(Dir.getwd,"my/templates/mytemplate") - end - - it "should use the environment templatedir if no module is found and an environment is specified" do - FileTest.stubs(:exist?).returns true - Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates"]) - Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" - end - - it "should use first dir from environment templatedir if no module is found and an environment is specified" do - FileTest.stubs(:exist?).returns true - Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates", "/two/templates"]) - Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" - end - - it "should use a valid dir when templatedir is a path for unqualified templates and the first dir contains template" do - Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) - FileTest.expects(:exist?).with("/one/templates/mytemplate").returns(true) - Puppet::Parser::Files.find_template("mytemplate").should == "/one/templates/mytemplate" - end - - it "should use a valid dir when templatedir is a path for unqualified templates and only second dir contains template" do - Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) - FileTest.expects(:exist?).with("/one/templates/mytemplate").returns(false) - FileTest.expects(:exist?).with("/two/templates/mytemplate").returns(true) - Puppet::Parser::Files.find_template("mytemplate").should == "/two/templates/mytemplate" - end - - it "should use the node environment if specified" do - mod = mock 'module' - Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod - - mod.expects(:template).returns("/my/modules/mymod/templates/envtemplate") - - Puppet::Parser::Files.find_template("mymod/envtemplate", "myenv").should == "/my/modules/mymod/templates/envtemplate" - end - - it "should return nil if no template can be found" do - Puppet::Parser::Files.find_template("foomod/envtemplate", "myenv").should be_nil - end - - after { Puppet.settings.clear } - end - - describe "when searching for manifests" do - it "should ignore invalid modules" do - mod = mock 'module' - Puppet::Node::Environment.new.expects(:module).with("mymod").raises(Puppet::Module::InvalidName, "name is invalid") - Puppet.expects(:value).with(:modulepath).never - Dir.stubs(:glob).returns %w{foo} - - Puppet::Parser::Files.find_manifests("mymod/init.pp") - end - end - - describe "when searching for manifests when no module is found" do - before do - File.stubs(:find).returns(nil) - end - - it "should not look for modules when paths are fully qualified" do - Puppet.expects(:value).with(:modulepath).never - file = @basepath + "/fully/qualified/file.pp" - Dir.stubs(:glob).with(file).returns([file]) - Puppet::Parser::Files.find_manifests(file) - end - - it "should return nil and an array of fully qualified files" do - file = @basepath + "/fully/qualified/file.pp" - Dir.stubs(:glob).with(file).returns([file]) - Puppet::Parser::Files.find_manifests(file).should == [nil, [file]] - end - - it "should match against provided fully qualified patterns" do - pattern = @basepath + "/fully/qualified/pattern/*" - Dir.expects(:glob).with(pattern).returns(%w{my file list}) - Puppet::Parser::Files.find_manifests(pattern)[1].should == %w{my file list} - end - - it "should look for files relative to the current directory" do - cwd = Dir.getwd - Dir.expects(:glob).with("#{cwd}/foobar/init.pp").returns(["#{cwd}/foobar/init.pp"]) - Puppet::Parser::Files.find_manifests("foobar/init.pp")[1].should == ["#{cwd}/foobar/init.pp"] - end - - it "should only return files, not directories" do - pattern = @basepath + "/fully/qualified/pattern/*" - file = @basepath + "/my/file" - dir = @basepath + "/my/directory" - Dir.expects(:glob).with(pattern).returns([file, dir]) - FileTest.expects(:directory?).with(file).returns(false) - FileTest.expects(:directory?).with(dir).returns(true) - Puppet::Parser::Files.find_manifests(pattern)[1].should == [file] - end - end - - describe "when searching for manifests in a found module" do - it "should return the name of the module and the manifests from the first found module" do - mod = Puppet::Module.new("mymod") - Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod - mod.expects(:match_manifests).with("init.pp").returns(%w{/one/mymod/manifests/init.pp}) - Puppet::Parser::Files.find_manifests("mymod/init.pp").should == ["mymod", ["/one/mymod/manifests/init.pp"]] - end - - it "should use the node environment if specified" do - mod = Puppet::Module.new("mymod") - Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod - mod.expects(:match_manifests).with("init.pp").returns(%w{/one/mymod/manifests/init.pp}) - Puppet::Parser::Files.find_manifests("mymod/init.pp", :environment => "myenv")[1].should == ["/one/mymod/manifests/init.pp"] - end - - after { Puppet.settings.clear } - end -end diff --git a/spec/unit/parser/functions/defined_spec.rb b/spec/unit/parser/functions/defined_spec.rb new file mode 100755 index 000000000..03b0ef9dd --- /dev/null +++ b/spec/unit/parser/functions/defined_spec.rb @@ -0,0 +1,50 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the 'defined' function" do + + before :each do + Puppet::Node::Environment.stubs(:current).returns(nil) + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) + @scope = Puppet::Parser::Scope.new(:compiler => @compiler) + end + + it "should exist" do + Puppet::Parser::Functions.function("defined").should == "function_defined" + end + + it "should be true when the name is defined as a class" do + @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "yayness") + @scope.function_defined("yayness").should be_true + end + + it "should be true when the name is defined as a definition" do + @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") + @scope.function_defined("yayness").should be_true + end + + it "should be true when the name is defined as a builtin type" do + @scope.function_defined("file").should be_true + end + + + it "should be true when any of the provided names are defined" do + @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") + @scope.function_defined(["meh", "yayness", "booness"]).should be_true + end + + it "should be false when a single given name is not defined" do + @scope.function_defined("meh").should be_false + end + + it "should be false when none of the names are defined" do + @scope.function_defined(["meh", "yayness", "booness"]).should be_false + end + + it "should be true when a resource reference is provided and the resource is in the catalog" do + resource = Puppet::Resource.new("file", "/my/file") + @compiler.add_resource(@scope, resource) + @scope.function_defined(resource).should be_true + end +end diff --git a/spec/unit/parser/functions/defined_spec_spec.rb b/spec/unit/parser/functions/defined_spec_spec.rb deleted file mode 100755 index 03b0ef9dd..000000000 --- a/spec/unit/parser/functions/defined_spec_spec.rb +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the 'defined' function" do - - before :each do - Puppet::Node::Environment.stubs(:current).returns(nil) - @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) - @scope = Puppet::Parser::Scope.new(:compiler => @compiler) - end - - it "should exist" do - Puppet::Parser::Functions.function("defined").should == "function_defined" - end - - it "should be true when the name is defined as a class" do - @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "yayness") - @scope.function_defined("yayness").should be_true - end - - it "should be true when the name is defined as a definition" do - @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") - @scope.function_defined("yayness").should be_true - end - - it "should be true when the name is defined as a builtin type" do - @scope.function_defined("file").should be_true - end - - - it "should be true when any of the provided names are defined" do - @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") - @scope.function_defined(["meh", "yayness", "booness"]).should be_true - end - - it "should be false when a single given name is not defined" do - @scope.function_defined("meh").should be_false - end - - it "should be false when none of the names are defined" do - @scope.function_defined(["meh", "yayness", "booness"]).should be_false - end - - it "should be true when a resource reference is provided and the resource is in the catalog" do - resource = Puppet::Resource.new("file", "/my/file") - @compiler.add_resource(@scope, resource) - @scope.function_defined(resource).should be_true - end -end diff --git a/spec/unit/parser/functions/fqdn_rand_spec.rb b/spec/unit/parser/functions/fqdn_rand_spec.rb new file mode 100644 index 000000000..cde899eef --- /dev/null +++ b/spec/unit/parser/functions/fqdn_rand_spec.rb @@ -0,0 +1,62 @@ +#! /usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the fqdn_rand function" do + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should exist" do + Puppet::Parser::Functions.function("fqdn_rand").should == "function_fqdn_rand" + end + + it "should handle 0 arguments" do + @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + lambda { @scope.function_fqdn_rand([]) }.should_not raise_error(Puppet::ParseError) + end + + it "should handle 1 argument'}" do + @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + lambda { @scope.function_fqdn_rand([3]) }.should_not raise_error(Puppet::ParseError) + end + + + (1..10).each { |n| + it "should handle #{n} additional arguments" do + @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + lambda { @scope.function_fqdn_rand([3,1,2,3,4,5,6,7,8,9,10][0..n]) }.should_not raise_error(Puppet::ParseError) + end + it "should handle #{n} additional string arguments" do + @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + lambda { @scope.function_fqdn_rand([3,%w{ 1 2 3 4 5 6 7 8 9 10}].flatten[0..n]) }.should_not raise_error(Puppet::ParseError) + end + } + + it "should return a value less than max" do + @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + @scope.function_fqdn_rand([3]).should satisfy {|n| n.to_i < 3 } + end + + it "should return the same values on subsequent invocations for the same host" do + @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1").twice + @scope.function_fqdn_rand([3,4]).should eql(@scope.function_fqdn_rand([3, 4])) + end + + it "should return different sequences of value for different hosts" do + @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + val1 = @scope.function_fqdn_rand([10000000,4]) + @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.2") + val2 = @scope.function_fqdn_rand([10000000,4]) + val1.should_not eql(val2) + end + + it "should return different values for the same hosts with different seeds" do + @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + val1 = @scope.function_fqdn_rand([10000000,4]) + @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + val2 = @scope.function_fqdn_rand([10000000,42]) + val1.should_not eql(val2) + end +end diff --git a/spec/unit/parser/functions/fqdn_rand_spec_spec.rb b/spec/unit/parser/functions/fqdn_rand_spec_spec.rb deleted file mode 100644 index cde899eef..000000000 --- a/spec/unit/parser/functions/fqdn_rand_spec_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -#! /usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the fqdn_rand function" do - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should exist" do - Puppet::Parser::Functions.function("fqdn_rand").should == "function_fqdn_rand" - end - - it "should handle 0 arguments" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") - lambda { @scope.function_fqdn_rand([]) }.should_not raise_error(Puppet::ParseError) - end - - it "should handle 1 argument'}" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") - lambda { @scope.function_fqdn_rand([3]) }.should_not raise_error(Puppet::ParseError) - end - - - (1..10).each { |n| - it "should handle #{n} additional arguments" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") - lambda { @scope.function_fqdn_rand([3,1,2,3,4,5,6,7,8,9,10][0..n]) }.should_not raise_error(Puppet::ParseError) - end - it "should handle #{n} additional string arguments" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") - lambda { @scope.function_fqdn_rand([3,%w{ 1 2 3 4 5 6 7 8 9 10}].flatten[0..n]) }.should_not raise_error(Puppet::ParseError) - end - } - - it "should return a value less than max" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") - @scope.function_fqdn_rand([3]).should satisfy {|n| n.to_i < 3 } - end - - it "should return the same values on subsequent invocations for the same host" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1").twice - @scope.function_fqdn_rand([3,4]).should eql(@scope.function_fqdn_rand([3, 4])) - end - - it "should return different sequences of value for different hosts" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") - val1 = @scope.function_fqdn_rand([10000000,4]) - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.2") - val2 = @scope.function_fqdn_rand([10000000,4]) - val1.should_not eql(val2) - end - - it "should return different values for the same hosts with different seeds" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") - val1 = @scope.function_fqdn_rand([10000000,4]) - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") - val2 = @scope.function_fqdn_rand([10000000,42]) - val1.should_not eql(val2) - end -end diff --git a/spec/unit/parser/functions/generate_spec.rb b/spec/unit/parser/functions/generate_spec.rb new file mode 100755 index 000000000..05c2cb7dc --- /dev/null +++ b/spec/unit/parser/functions/generate_spec.rb @@ -0,0 +1,41 @@ +#! /usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the generate function" do + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should exist" do + Puppet::Parser::Functions.function("generate").should == "function_generate" + end + + it "should accept a fully-qualified path as a command" do + command = File::SEPARATOR + "command" + Puppet::Util.expects(:execute).with([command]).returns("yay") + lambda { @scope.function_generate([command]) }.should_not raise_error(Puppet::ParseError) + end + + it "should not accept a relative path as a command" do + command = "command" + lambda { @scope.function_generate([command]) }.should raise_error(Puppet::ParseError) + end + + # Really not sure how to implement this test, just sure it needs + # to be implemented. + it "should not accept a command containing illegal characters" + + it "should not accept a command containing '..'" do + command = File::SEPARATOR + "command" + File::SEPARATOR + ".." + File::SEPARATOR + lambda { @scope.function_generate([command]) }.should raise_error(Puppet::ParseError) + end + + it "should execute the generate script with the correct working directory" do + command = File::SEPARATOR + "command" + Dir.expects(:chdir).with(File.dirname(command)).yields + Puppet::Util.expects(:execute).with([command]).returns("yay") + lambda { @scope.function_generate([command]) }.should_not raise_error(Puppet::ParseError) + end +end diff --git a/spec/unit/parser/functions/generate_spec_spec.rb b/spec/unit/parser/functions/generate_spec_spec.rb deleted file mode 100755 index 05c2cb7dc..000000000 --- a/spec/unit/parser/functions/generate_spec_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -#! /usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the generate function" do - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should exist" do - Puppet::Parser::Functions.function("generate").should == "function_generate" - end - - it "should accept a fully-qualified path as a command" do - command = File::SEPARATOR + "command" - Puppet::Util.expects(:execute).with([command]).returns("yay") - lambda { @scope.function_generate([command]) }.should_not raise_error(Puppet::ParseError) - end - - it "should not accept a relative path as a command" do - command = "command" - lambda { @scope.function_generate([command]) }.should raise_error(Puppet::ParseError) - end - - # Really not sure how to implement this test, just sure it needs - # to be implemented. - it "should not accept a command containing illegal characters" - - it "should not accept a command containing '..'" do - command = File::SEPARATOR + "command" + File::SEPARATOR + ".." + File::SEPARATOR - lambda { @scope.function_generate([command]) }.should raise_error(Puppet::ParseError) - end - - it "should execute the generate script with the correct working directory" do - command = File::SEPARATOR + "command" - Dir.expects(:chdir).with(File.dirname(command)).yields - Puppet::Util.expects(:execute).with([command]).returns("yay") - lambda { @scope.function_generate([command]) }.should_not raise_error(Puppet::ParseError) - end -end diff --git a/spec/unit/parser/functions/inline_template_spec.rb b/spec/unit/parser/functions/inline_template_spec.rb new file mode 100755 index 000000000..19e1a3b2a --- /dev/null +++ b/spec/unit/parser/functions/inline_template_spec.rb @@ -0,0 +1,59 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the inline_template function" do + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should exist" do + Puppet::Parser::Functions.function("inline_template").should == "function_inline_template" + end + + it "should create a TemplateWrapper when called" do + tw = stub_everything 'template_wrapper' + + Puppet::Parser::TemplateWrapper.expects(:new).returns(tw) + + @scope.function_inline_template("test") + end + + it "should pass the template string to TemplateWrapper.result" do + tw = stub_everything 'template_wrapper' + Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) + + tw.expects(:result).with("test") + + @scope.function_inline_template("test") + end + + it "should return what TemplateWrapper.result returns" do + tw = stub_everything 'template_wrapper' + Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) + + tw.expects(:result).returns("template contents evaluated") + + @scope.function_inline_template("test").should == "template contents evaluated" + end + + it "should concatenate template wrapper outputs for multiple templates" do + tw1 = stub_everything "template_wrapper1" + tw2 = stub_everything "template_wrapper2" + Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw1,tw2) + tw1.stubs(:result).returns("result1") + tw2.stubs(:result).returns("result2") + + @scope.function_inline_template(["1","2"]).should == "result1result2" + end + + it "should raise an error if the template raises an error" do + tw = stub_everything 'template_wrapper' + Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) + tw.stubs(:result).raises + + lambda { @scope.function_inline_template("1") }.should raise_error(Puppet::ParseError) + end + +end \ No newline at end of file diff --git a/spec/unit/parser/functions/inline_template_spec_spec.rb b/spec/unit/parser/functions/inline_template_spec_spec.rb deleted file mode 100755 index 19e1a3b2a..000000000 --- a/spec/unit/parser/functions/inline_template_spec_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the inline_template function" do - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should exist" do - Puppet::Parser::Functions.function("inline_template").should == "function_inline_template" - end - - it "should create a TemplateWrapper when called" do - tw = stub_everything 'template_wrapper' - - Puppet::Parser::TemplateWrapper.expects(:new).returns(tw) - - @scope.function_inline_template("test") - end - - it "should pass the template string to TemplateWrapper.result" do - tw = stub_everything 'template_wrapper' - Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) - - tw.expects(:result).with("test") - - @scope.function_inline_template("test") - end - - it "should return what TemplateWrapper.result returns" do - tw = stub_everything 'template_wrapper' - Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) - - tw.expects(:result).returns("template contents evaluated") - - @scope.function_inline_template("test").should == "template contents evaluated" - end - - it "should concatenate template wrapper outputs for multiple templates" do - tw1 = stub_everything "template_wrapper1" - tw2 = stub_everything "template_wrapper2" - Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw1,tw2) - tw1.stubs(:result).returns("result1") - tw2.stubs(:result).returns("result2") - - @scope.function_inline_template(["1","2"]).should == "result1result2" - end - - it "should raise an error if the template raises an error" do - tw = stub_everything 'template_wrapper' - Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) - tw.stubs(:result).raises - - lambda { @scope.function_inline_template("1") }.should raise_error(Puppet::ParseError) - end - -end \ No newline at end of file diff --git a/spec/unit/parser/functions/realize_spec.rb b/spec/unit/parser/functions/realize_spec.rb new file mode 100755 index 000000000..d9c94b143 --- /dev/null +++ b/spec/unit/parser/functions/realize_spec.rb @@ -0,0 +1,51 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the realize function" do + + before :each do + @collector = stub_everything 'collector' + @scope = Puppet::Parser::Scope.new() + @compiler = stub 'compiler' + @compiler.stubs(:add_collection).with(@collector) + @scope.stubs(:compiler).returns(@compiler) + end + + it "should exist" do + Puppet::Parser::Functions.function("realize").should == "function_realize" + end + + it "should create a Collector when called" do + + Puppet::Parser::Collector.expects(:new).returns(@collector) + + @scope.function_realize("test") + end + + it "should assign the passed-in resources to the collector" do + Puppet::Parser::Collector.stubs(:new).returns(@collector) + + @collector.expects(:resources=).with(["test"]) + + @scope.function_realize("test") + end + + it "should flatten the resources assigned to the collector" do + Puppet::Parser::Collector.stubs(:new).returns(@collector) + + @collector.expects(:resources=).with(["test"]) + + @scope.function_realize([["test"]]) + end + + it "should let the compiler know this collector" do + Puppet::Parser::Collector.stubs(:new).returns(@collector) + @collector.stubs(:resources=).with(["test"]) + + @compiler.expects(:add_collection).with(@collector) + + @scope.function_realize("test") + end + +end diff --git a/spec/unit/parser/functions/realize_spec_spec.rb b/spec/unit/parser/functions/realize_spec_spec.rb deleted file mode 100755 index d9c94b143..000000000 --- a/spec/unit/parser/functions/realize_spec_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the realize function" do - - before :each do - @collector = stub_everything 'collector' - @scope = Puppet::Parser::Scope.new() - @compiler = stub 'compiler' - @compiler.stubs(:add_collection).with(@collector) - @scope.stubs(:compiler).returns(@compiler) - end - - it "should exist" do - Puppet::Parser::Functions.function("realize").should == "function_realize" - end - - it "should create a Collector when called" do - - Puppet::Parser::Collector.expects(:new).returns(@collector) - - @scope.function_realize("test") - end - - it "should assign the passed-in resources to the collector" do - Puppet::Parser::Collector.stubs(:new).returns(@collector) - - @collector.expects(:resources=).with(["test"]) - - @scope.function_realize("test") - end - - it "should flatten the resources assigned to the collector" do - Puppet::Parser::Collector.stubs(:new).returns(@collector) - - @collector.expects(:resources=).with(["test"]) - - @scope.function_realize([["test"]]) - end - - it "should let the compiler know this collector" do - Puppet::Parser::Collector.stubs(:new).returns(@collector) - @collector.stubs(:resources=).with(["test"]) - - @compiler.expects(:add_collection).with(@collector) - - @scope.function_realize("test") - end - -end diff --git a/spec/unit/parser/functions/regsubst_spec.rb b/spec/unit/parser/functions/regsubst_spec.rb new file mode 100755 index 000000000..5a533efb1 --- /dev/null +++ b/spec/unit/parser/functions/regsubst_spec.rb @@ -0,0 +1,168 @@ +#! /usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the regsubst function" do + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should exist" do + Puppet::Parser::Functions.function("regsubst").should == "function_regsubst" + end + + it "should raise a ParseError if there is less than 3 arguments" do + lambda { @scope.function_regsubst(["foo", "bar"]) }.should( + raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError if there is more than 5 arguments" do + lambda { @scope.function_regsubst(["foo", "bar", "gazonk", "del", "x", "y"]) }.should( + raise_error(Puppet::ParseError)) + end + + + it "should raise a ParseError when given a bad flag" do + lambda { @scope.function_regsubst(["foo", "bar", "gazonk", "X"]) }.should( + raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError for non-string and non-array target" do + lambda { @scope.function_regsubst([4711, "bar", "gazonk"]) }.should( + raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError for array target with non-string element" do + lambda { @scope.function_regsubst([["x", ["y"], "z"], "bar", "gazonk"]) }.should( + raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError for a bad regular expression" do + lambda { @scope.function_regsubst(["foo", "(bar", "gazonk"]) }.should( + raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError for a non-string regular expression" do + lambda { @scope.function_regsubst(["foo", ["bar"], "gazonk"]) }.should( + raise_error(Puppet::ParseError)) + end + + it "should handle groups" do + result = @scope.function_regsubst( + [ '130.236.254.10', + '^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$', + '\4-\3-\2-\1' + ]) + result.should(eql("10-254-236-130")) + end + + it "should handle simple regexps" do + result = @scope.function_regsubst( + [ "the monkey breaks banana trees", + "b[an]*a", + "coconut" + ]) + result.should(eql("the monkey breaks coconut trees")) + end + + it "should handle case-sensitive regexps" do + result = @scope.function_regsubst( + [ "the monkey breaks baNAna trees", + "b[an]+a", + "coconut" + ]) + result.should(eql("the monkey breaks baNAna trees")) + end + + it "should handle case-insensitive regexps" do + result = @scope.function_regsubst( + [ "the monkey breaks baNAna trees", + "b[an]+a", + "coconut", + "I" + ]) + result.should(eql("the monkey breaks coconut trees")) + end + + it "should handle global substitutions" do + result = @scope.function_regsubst( + [ "the monkey breaks\tbanana trees", + "[ \t]", + "--", + "G" + ]) + result.should(eql("the--monkey--breaks--banana--trees")) + end + + it "should handle global substitutions with groups" do + result = @scope.function_regsubst( + [ '130.236.254.10', + '([0-9]+)', + '<\1>', + 'G' + ]) + result.should(eql('<130>.<236>.<254>.<10>')) + end + + it "should apply on all elements of an array" do + data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] + result = @scope.function_regsubst([ data, '[.]', '-']) + result.should(eql( + ['130-236.254.10', 'foo-example.com', 'coconut', '10-20.30.40'])) + end + + it "should apply global substitutions on all elements of an array" do + data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] + result = @scope.function_regsubst([ data, '[.]', '-', 'G']) + result.should(eql( + ['130-236-254-10', 'foo-example-com', 'coconut', '10-20-30-40'])) + end + + it "should handle groups on all elements of an array" do + data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] + result = @scope.function_regsubst( + [ data, + '^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$', + '\4-\3-\2-\1' + ]) + result.should(eql( + ['10-254-236-130', 'foo.example.com', 'coconut', '40-30-20-10'])) + end + + it "should handle global substitutions with groups on all elements of an array" do + data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] + result = @scope.function_regsubst( + [ data, + '([^.]+)', + '<\1>', + 'G' + ]) + result.should(eql( + ['<130>.<236>.<254>.<10>', '..', + '', '<10>.<20>.<30>.<40>'])) + end + + it "should return an array (not a string) for a single element array parameter" do + data = ['130.236.254.10'] + result = @scope.function_regsubst( + [ data, + '([^.]+)', + '<\1>', + 'G' + ]) + result.should(eql(['<130>.<236>.<254>.<10>'])) + end + + it "should return a string (not a one element array) for a simple string parameter" do + data = '130.236.254.10' + result = @scope.function_regsubst( + [ data, + '([^.]+)', + '<\1>', + 'G' + ]) + result.should(eql('<130>.<236>.<254>.<10>')) + end + +end diff --git a/spec/unit/parser/functions/regsubst_spec_spec.rb b/spec/unit/parser/functions/regsubst_spec_spec.rb deleted file mode 100755 index 5a533efb1..000000000 --- a/spec/unit/parser/functions/regsubst_spec_spec.rb +++ /dev/null @@ -1,168 +0,0 @@ -#! /usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the regsubst function" do - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should exist" do - Puppet::Parser::Functions.function("regsubst").should == "function_regsubst" - end - - it "should raise a ParseError if there is less than 3 arguments" do - lambda { @scope.function_regsubst(["foo", "bar"]) }.should( - raise_error(Puppet::ParseError)) - end - - it "should raise a ParseError if there is more than 5 arguments" do - lambda { @scope.function_regsubst(["foo", "bar", "gazonk", "del", "x", "y"]) }.should( - raise_error(Puppet::ParseError)) - end - - - it "should raise a ParseError when given a bad flag" do - lambda { @scope.function_regsubst(["foo", "bar", "gazonk", "X"]) }.should( - raise_error(Puppet::ParseError)) - end - - it "should raise a ParseError for non-string and non-array target" do - lambda { @scope.function_regsubst([4711, "bar", "gazonk"]) }.should( - raise_error(Puppet::ParseError)) - end - - it "should raise a ParseError for array target with non-string element" do - lambda { @scope.function_regsubst([["x", ["y"], "z"], "bar", "gazonk"]) }.should( - raise_error(Puppet::ParseError)) - end - - it "should raise a ParseError for a bad regular expression" do - lambda { @scope.function_regsubst(["foo", "(bar", "gazonk"]) }.should( - raise_error(Puppet::ParseError)) - end - - it "should raise a ParseError for a non-string regular expression" do - lambda { @scope.function_regsubst(["foo", ["bar"], "gazonk"]) }.should( - raise_error(Puppet::ParseError)) - end - - it "should handle groups" do - result = @scope.function_regsubst( - [ '130.236.254.10', - '^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$', - '\4-\3-\2-\1' - ]) - result.should(eql("10-254-236-130")) - end - - it "should handle simple regexps" do - result = @scope.function_regsubst( - [ "the monkey breaks banana trees", - "b[an]*a", - "coconut" - ]) - result.should(eql("the monkey breaks coconut trees")) - end - - it "should handle case-sensitive regexps" do - result = @scope.function_regsubst( - [ "the monkey breaks baNAna trees", - "b[an]+a", - "coconut" - ]) - result.should(eql("the monkey breaks baNAna trees")) - end - - it "should handle case-insensitive regexps" do - result = @scope.function_regsubst( - [ "the monkey breaks baNAna trees", - "b[an]+a", - "coconut", - "I" - ]) - result.should(eql("the monkey breaks coconut trees")) - end - - it "should handle global substitutions" do - result = @scope.function_regsubst( - [ "the monkey breaks\tbanana trees", - "[ \t]", - "--", - "G" - ]) - result.should(eql("the--monkey--breaks--banana--trees")) - end - - it "should handle global substitutions with groups" do - result = @scope.function_regsubst( - [ '130.236.254.10', - '([0-9]+)', - '<\1>', - 'G' - ]) - result.should(eql('<130>.<236>.<254>.<10>')) - end - - it "should apply on all elements of an array" do - data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] - result = @scope.function_regsubst([ data, '[.]', '-']) - result.should(eql( - ['130-236.254.10', 'foo-example.com', 'coconut', '10-20.30.40'])) - end - - it "should apply global substitutions on all elements of an array" do - data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] - result = @scope.function_regsubst([ data, '[.]', '-', 'G']) - result.should(eql( - ['130-236-254-10', 'foo-example-com', 'coconut', '10-20-30-40'])) - end - - it "should handle groups on all elements of an array" do - data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] - result = @scope.function_regsubst( - [ data, - '^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$', - '\4-\3-\2-\1' - ]) - result.should(eql( - ['10-254-236-130', 'foo.example.com', 'coconut', '40-30-20-10'])) - end - - it "should handle global substitutions with groups on all elements of an array" do - data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] - result = @scope.function_regsubst( - [ data, - '([^.]+)', - '<\1>', - 'G' - ]) - result.should(eql( - ['<130>.<236>.<254>.<10>', '..', - '', '<10>.<20>.<30>.<40>'])) - end - - it "should return an array (not a string) for a single element array parameter" do - data = ['130.236.254.10'] - result = @scope.function_regsubst( - [ data, - '([^.]+)', - '<\1>', - 'G' - ]) - result.should(eql(['<130>.<236>.<254>.<10>'])) - end - - it "should return a string (not a one element array) for a simple string parameter" do - data = '130.236.254.10' - result = @scope.function_regsubst( - [ data, - '([^.]+)', - '<\1>', - 'G' - ]) - result.should(eql('<130>.<236>.<254>.<10>')) - end - -end diff --git a/spec/unit/parser/functions/require_spec.rb b/spec/unit/parser/functions/require_spec.rb new file mode 100755 index 000000000..45769ccbe --- /dev/null +++ b/spec/unit/parser/functions/require_spec.rb @@ -0,0 +1,74 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the require function" do + + before :each do + @catalog = stub 'catalog' + @compiler = stub 'compiler', :catalog => @catalog + + @scope = Puppet::Parser::Scope.new() + @scope.stubs(:findresource) + @scope.stubs(:compiler).returns(@compiler) + @klass = stub 'class', :name => "myclass" + @scope.stubs(:find_hostclass).returns(@klass) + + @resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope, :source => "source") + @resource.stubs(:metaparam_compatibility_mode?).returns false + @scope.stubs(:resource).returns @resource + end + + it "should exist" do + Puppet::Parser::Functions.function("require").should == "function_require" + end + + it "should delegate to the 'include' puppet function" do + @scope.expects(:function_include).with("myclass") + + @scope.function_require("myclass") + end + + it "should set the 'require' prarameter on the resource to a resource reference" do + @scope.stubs(:function_include) + @scope.function_require("myclass") + + @resource["require"].should be_instance_of(Array) + @resource["require"][0].should be_instance_of(Puppet::Resource) + end + + it "should verify the 'include' function is loaded" do + Puppet::Parser::Functions.expects(:function).with(:include).returns(:function_include) + @scope.stubs(:function_include) + @scope.function_require("myclass") + end + + it "should include the class but not add a dependency if used on a client not at least version 0.25" do + @resource.expects(:metaparam_compatibility_mode?).returns true + @scope.expects(:warning) + @resource.expects(:set_parameter).never + @scope.expects(:function_include) + + @scope.function_require("myclass") + end + + it "should lookup the absolute class path" do + @scope.stubs(:function_include) + + @scope.expects(:find_hostclass).with("myclass").returns(@klass) + @klass.expects(:name).returns("myclass") + + @scope.function_require("myclass") + end + + it "should append the required class to the require parameter" do + @scope.stubs(:function_include) + + one = Puppet::Resource.new(:file, "/one") + @resource[:require] = one + @scope.function_require("myclass") + + @resource[:require].should be_include(one) + @resource[:require].detect { |r| r.to_s == "Class[Myclass]" }.should be_instance_of(Puppet::Resource) + end +end diff --git a/spec/unit/parser/functions/require_spec_spec.rb b/spec/unit/parser/functions/require_spec_spec.rb deleted file mode 100755 index 45769ccbe..000000000 --- a/spec/unit/parser/functions/require_spec_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the require function" do - - before :each do - @catalog = stub 'catalog' - @compiler = stub 'compiler', :catalog => @catalog - - @scope = Puppet::Parser::Scope.new() - @scope.stubs(:findresource) - @scope.stubs(:compiler).returns(@compiler) - @klass = stub 'class', :name => "myclass" - @scope.stubs(:find_hostclass).returns(@klass) - - @resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope, :source => "source") - @resource.stubs(:metaparam_compatibility_mode?).returns false - @scope.stubs(:resource).returns @resource - end - - it "should exist" do - Puppet::Parser::Functions.function("require").should == "function_require" - end - - it "should delegate to the 'include' puppet function" do - @scope.expects(:function_include).with("myclass") - - @scope.function_require("myclass") - end - - it "should set the 'require' prarameter on the resource to a resource reference" do - @scope.stubs(:function_include) - @scope.function_require("myclass") - - @resource["require"].should be_instance_of(Array) - @resource["require"][0].should be_instance_of(Puppet::Resource) - end - - it "should verify the 'include' function is loaded" do - Puppet::Parser::Functions.expects(:function).with(:include).returns(:function_include) - @scope.stubs(:function_include) - @scope.function_require("myclass") - end - - it "should include the class but not add a dependency if used on a client not at least version 0.25" do - @resource.expects(:metaparam_compatibility_mode?).returns true - @scope.expects(:warning) - @resource.expects(:set_parameter).never - @scope.expects(:function_include) - - @scope.function_require("myclass") - end - - it "should lookup the absolute class path" do - @scope.stubs(:function_include) - - @scope.expects(:find_hostclass).with("myclass").returns(@klass) - @klass.expects(:name).returns("myclass") - - @scope.function_require("myclass") - end - - it "should append the required class to the require parameter" do - @scope.stubs(:function_include) - - one = Puppet::Resource.new(:file, "/one") - @resource[:require] = one - @scope.function_require("myclass") - - @resource[:require].should be_include(one) - @resource[:require].detect { |r| r.to_s == "Class[Myclass]" }.should be_instance_of(Puppet::Resource) - end -end diff --git a/spec/unit/parser/functions/shellquote_spec.rb b/spec/unit/parser/functions/shellquote_spec.rb new file mode 100755 index 000000000..283a4de1e --- /dev/null +++ b/spec/unit/parser/functions/shellquote_spec.rb @@ -0,0 +1,92 @@ +#! /usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the shellquote function" do + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should exist" do + Puppet::Parser::Functions.function("shellquote").should == "function_shellquote" + end + + + it "should handle no arguments" do + result = @scope.function_shellquote([]) + result.should(eql("")) + end + + it "should handle several simple arguments" do + result = @scope.function_shellquote( + ['foo', 'bar@example.com', 'localhost:/dev/null', 'xyzzy+-4711,23']) + result.should(eql( + 'foo bar@example.com localhost:/dev/null xyzzy+-4711,23')) + end + + it "should handle array arguments" do + result = @scope.function_shellquote( + ['foo', ['bar@example.com', 'localhost:/dev/null'], + 'xyzzy+-4711,23']) + result.should(eql( + 'foo bar@example.com localhost:/dev/null xyzzy+-4711,23')) + end + + it "should quote unsafe characters" do + result = @scope.function_shellquote( + ['/etc/passwd ', '(ls)', '*', '[?]', "'&'"]) + result.should(eql( + '"/etc/passwd " "(ls)" "*" "[?]" "\'&\'"')) + end + + it "should deal with double quotes" do + result = @scope.function_shellquote( + ['"foo"bar"']) + result.should(eql( + '\'"foo"bar"\'')) + end + + it "should cope with dollar signs" do + result = @scope.function_shellquote( + ['$PATH', 'foo$bar', '"x$"']) + result.should(eql( + "'$PATH' 'foo$bar' '\"x$\"'")) + end + + it "should deal with apostrophes (single quotes)" do + result = @scope.function_shellquote( + ["'foo'bar'", "`$'EDITOR'`"]) + result.should(eql( + '"\'foo\'bar\'" "\\`\\$\'EDITOR\'\\`"')) + end + + it "should cope with grave accents (backquotes)" do + result = @scope.function_shellquote( + ['`echo *`', '`ls "$MAILPATH"`']) + result.should(eql( + "'`echo *`' '`ls \"$MAILPATH\"`'")) + end + + it "should deal with both single and double quotes" do + result = @scope.function_shellquote( + ['\'foo"bar"xyzzy\'', '"foo\'bar\'xyzzy"']) + result.should(eql( + '"\'foo\\"bar\\"xyzzy\'" "\\"foo\'bar\'xyzzy\\""')) + end + + it "should handle multiple quotes *and* dollars and backquotes" do + result = @scope.function_shellquote( + ['\'foo"$x`bar`"xyzzy\'']) + result.should(eql( + '"\'foo\\"\\$x\\`bar\\`\\"xyzzy\'"')) + end + + it "should handle linefeeds" do + result = @scope.function_shellquote( + ["foo \n bar"]) + result.should(eql( + "\"foo \n bar\"")) + end + +end diff --git a/spec/unit/parser/functions/shellquote_spec_spec.rb b/spec/unit/parser/functions/shellquote_spec_spec.rb deleted file mode 100755 index 283a4de1e..000000000 --- a/spec/unit/parser/functions/shellquote_spec_spec.rb +++ /dev/null @@ -1,92 +0,0 @@ -#! /usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the shellquote function" do - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should exist" do - Puppet::Parser::Functions.function("shellquote").should == "function_shellquote" - end - - - it "should handle no arguments" do - result = @scope.function_shellquote([]) - result.should(eql("")) - end - - it "should handle several simple arguments" do - result = @scope.function_shellquote( - ['foo', 'bar@example.com', 'localhost:/dev/null', 'xyzzy+-4711,23']) - result.should(eql( - 'foo bar@example.com localhost:/dev/null xyzzy+-4711,23')) - end - - it "should handle array arguments" do - result = @scope.function_shellquote( - ['foo', ['bar@example.com', 'localhost:/dev/null'], - 'xyzzy+-4711,23']) - result.should(eql( - 'foo bar@example.com localhost:/dev/null xyzzy+-4711,23')) - end - - it "should quote unsafe characters" do - result = @scope.function_shellquote( - ['/etc/passwd ', '(ls)', '*', '[?]', "'&'"]) - result.should(eql( - '"/etc/passwd " "(ls)" "*" "[?]" "\'&\'"')) - end - - it "should deal with double quotes" do - result = @scope.function_shellquote( - ['"foo"bar"']) - result.should(eql( - '\'"foo"bar"\'')) - end - - it "should cope with dollar signs" do - result = @scope.function_shellquote( - ['$PATH', 'foo$bar', '"x$"']) - result.should(eql( - "'$PATH' 'foo$bar' '\"x$\"'")) - end - - it "should deal with apostrophes (single quotes)" do - result = @scope.function_shellquote( - ["'foo'bar'", "`$'EDITOR'`"]) - result.should(eql( - '"\'foo\'bar\'" "\\`\\$\'EDITOR\'\\`"')) - end - - it "should cope with grave accents (backquotes)" do - result = @scope.function_shellquote( - ['`echo *`', '`ls "$MAILPATH"`']) - result.should(eql( - "'`echo *`' '`ls \"$MAILPATH\"`'")) - end - - it "should deal with both single and double quotes" do - result = @scope.function_shellquote( - ['\'foo"bar"xyzzy\'', '"foo\'bar\'xyzzy"']) - result.should(eql( - '"\'foo\\"bar\\"xyzzy\'" "\\"foo\'bar\'xyzzy\\""')) - end - - it "should handle multiple quotes *and* dollars and backquotes" do - result = @scope.function_shellquote( - ['\'foo"$x`bar`"xyzzy\'']) - result.should(eql( - '"\'foo\\"\\$x\\`bar\\`\\"xyzzy\'"')) - end - - it "should handle linefeeds" do - result = @scope.function_shellquote( - ["foo \n bar"]) - result.should(eql( - "\"foo \n bar\"")) - end - -end diff --git a/spec/unit/parser/functions/split_spec.rb b/spec/unit/parser/functions/split_spec.rb new file mode 100755 index 000000000..8aa031d19 --- /dev/null +++ b/spec/unit/parser/functions/split_spec.rb @@ -0,0 +1,51 @@ +#! /usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the split function" do + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should exist" do + Puppet::Parser::Functions.function("split").should == "function_split" + end + + it "should raise a ParseError if there is less than 2 arguments" do + lambda { @scope.function_split(["foo"]) }.should( + raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError if there is more than 2 arguments" do + lambda { @scope.function_split(["foo", "bar", "gazonk"]) }.should( + raise_error(Puppet::ParseError)) + end + + it "should raise a RegexpError if the regexp is malformed" do + lambda { @scope.function_split(["foo", "("]) }.should( + raise_error(RegexpError)) + end + + + it "should handle simple string without metacharacters" do + result = @scope.function_split([ "130;236;254;10", ";"]) + result.should(eql(["130", "236", "254", "10"])) + end + + it "should handle simple regexps" do + result = @scope.function_split([ "130.236;254.;10", "[.;]+"]) + result.should(eql(["130", "236", "254", "10"])) + end + + it "should handle groups" do + result = @scope.function_split([ "130.236;254.;10", "([.;]+)"]) + result.should(eql(["130", ".", "236", ";", "254", ".;", "10"])) + end + + it "should handle simple string without metacharacters" do + result = @scope.function_split([ "130.236.254.10", ";"]) + result.should(eql(["130.236.254.10"])) + end + +end diff --git a/spec/unit/parser/functions/split_spec_spec.rb b/spec/unit/parser/functions/split_spec_spec.rb deleted file mode 100755 index 8aa031d19..000000000 --- a/spec/unit/parser/functions/split_spec_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -#! /usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the split function" do - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should exist" do - Puppet::Parser::Functions.function("split").should == "function_split" - end - - it "should raise a ParseError if there is less than 2 arguments" do - lambda { @scope.function_split(["foo"]) }.should( - raise_error(Puppet::ParseError)) - end - - it "should raise a ParseError if there is more than 2 arguments" do - lambda { @scope.function_split(["foo", "bar", "gazonk"]) }.should( - raise_error(Puppet::ParseError)) - end - - it "should raise a RegexpError if the regexp is malformed" do - lambda { @scope.function_split(["foo", "("]) }.should( - raise_error(RegexpError)) - end - - - it "should handle simple string without metacharacters" do - result = @scope.function_split([ "130;236;254;10", ";"]) - result.should(eql(["130", "236", "254", "10"])) - end - - it "should handle simple regexps" do - result = @scope.function_split([ "130.236;254.;10", "[.;]+"]) - result.should(eql(["130", "236", "254", "10"])) - end - - it "should handle groups" do - result = @scope.function_split([ "130.236;254.;10", "([.;]+)"]) - result.should(eql(["130", ".", "236", ";", "254", ".;", "10"])) - end - - it "should handle simple string without metacharacters" do - result = @scope.function_split([ "130.236.254.10", ";"]) - result.should(eql(["130.236.254.10"])) - end - -end diff --git a/spec/unit/parser/functions/sprintf_spec.rb b/spec/unit/parser/functions/sprintf_spec.rb new file mode 100755 index 000000000..949dc3fcc --- /dev/null +++ b/spec/unit/parser/functions/sprintf_spec.rb @@ -0,0 +1,42 @@ +#! /usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the sprintf function" do + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should exist" do + Puppet::Parser::Functions.function("sprintf").should == "function_sprintf" + end + + it "should raise a ParseError if there is less than 1 argument" do + lambda { @scope.function_sprintf([]) }.should( + raise_error(Puppet::ParseError)) + end + + it "should format integers" do + result = @scope.function_sprintf(["%+05d", "23"]) + result.should(eql("+0023")) + end + + it "should format floats" do + result = @scope.function_sprintf(["%+.2f", "2.7182818284590451"]) + result.should(eql("+2.72")) + end + + it "should format large floats" do + result = @scope.function_sprintf(["%+.2e", "27182818284590451"]) + result.should(eql("+2.72e+16")) + end + + it "should perform more complex formatting" do + result = @scope.function_sprintf( + [ "<%.8s:%#5o %#8X (%-8s)>", + "overlongstring", "23", "48879", "foo" ]) + result.should(eql("")) + end + +end diff --git a/spec/unit/parser/functions/sprintf_spec_spec.rb b/spec/unit/parser/functions/sprintf_spec_spec.rb deleted file mode 100755 index 949dc3fcc..000000000 --- a/spec/unit/parser/functions/sprintf_spec_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -#! /usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the sprintf function" do - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should exist" do - Puppet::Parser::Functions.function("sprintf").should == "function_sprintf" - end - - it "should raise a ParseError if there is less than 1 argument" do - lambda { @scope.function_sprintf([]) }.should( - raise_error(Puppet::ParseError)) - end - - it "should format integers" do - result = @scope.function_sprintf(["%+05d", "23"]) - result.should(eql("+0023")) - end - - it "should format floats" do - result = @scope.function_sprintf(["%+.2f", "2.7182818284590451"]) - result.should(eql("+2.72")) - end - - it "should format large floats" do - result = @scope.function_sprintf(["%+.2e", "27182818284590451"]) - result.should(eql("+2.72e+16")) - end - - it "should perform more complex formatting" do - result = @scope.function_sprintf( - [ "<%.8s:%#5o %#8X (%-8s)>", - "overlongstring", "23", "48879", "foo" ]) - result.should(eql("")) - end - -end diff --git a/spec/unit/parser/functions/tag_spec.rb b/spec/unit/parser/functions/tag_spec.rb new file mode 100755 index 000000000..5fb467e59 --- /dev/null +++ b/spec/unit/parser/functions/tag_spec.rb @@ -0,0 +1,24 @@ +#! /usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the 'tag' function" do + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should exist" do + Puppet::Parser::Functions.function(:tag).should == "function_tag" + end + + it "should tag the resource with any provided tags" do + resource = Puppet::Parser::Resource.new(:file, "/file", :scope => @scope) + @scope.expects(:resource).returns resource + + @scope.function_tag ["one", "two"] + + resource.should be_tagged("one") + resource.should be_tagged("two") + end +end diff --git a/spec/unit/parser/functions/tag_spec_spec.rb b/spec/unit/parser/functions/tag_spec_spec.rb deleted file mode 100755 index 5fb467e59..000000000 --- a/spec/unit/parser/functions/tag_spec_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -#! /usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the 'tag' function" do - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should exist" do - Puppet::Parser::Functions.function(:tag).should == "function_tag" - end - - it "should tag the resource with any provided tags" do - resource = Puppet::Parser::Resource.new(:file, "/file", :scope => @scope) - @scope.expects(:resource).returns resource - - @scope.function_tag ["one", "two"] - - resource.should be_tagged("one") - resource.should be_tagged("two") - end -end diff --git a/spec/unit/parser/functions/template_spec.rb b/spec/unit/parser/functions/template_spec.rb new file mode 100755 index 000000000..8fc64d0c3 --- /dev/null +++ b/spec/unit/parser/functions/template_spec.rb @@ -0,0 +1,62 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the template function" do + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should exist" do + Puppet::Parser::Functions.function("template").should == "function_template" + end + + it "should create a TemplateWrapper when called" do + tw = stub_everything 'template_wrapper' + + Puppet::Parser::TemplateWrapper.expects(:new).returns(tw) + + @scope.function_template("test") + end + + it "should give the template filename to the TemplateWrapper" do + tw = stub_everything 'template_wrapper' + Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) + + tw.expects(:file=).with("test") + + @scope.function_template("test") + end + + it "should return what TemplateWrapper.result returns" do + tw = stub_everything 'template_wrapper' + Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) + tw.stubs(:file=).with("test") + + tw.expects(:result).returns("template contents evaluated") + + @scope.function_template("test").should == "template contents evaluated" + end + + it "should concatenate template wrapper outputs for multiple templates" do + tw1 = stub_everything "template_wrapper1" + tw2 = stub_everything "template_wrapper2" + Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw1,tw2) + tw1.stubs(:file=).with("1") + tw2.stubs(:file=).with("2") + tw1.stubs(:result).returns("result1") + tw2.stubs(:result).returns("result2") + + @scope.function_template(["1","2"]).should == "result1result2" + end + + it "should raise an error if the template raises an error" do + tw = stub_everything 'template_wrapper' + Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) + tw.stubs(:result).raises + + lambda { @scope.function_template("1") }.should raise_error(Puppet::ParseError) + end + +end \ No newline at end of file diff --git a/spec/unit/parser/functions/template_spec_spec.rb b/spec/unit/parser/functions/template_spec_spec.rb deleted file mode 100755 index 8fc64d0c3..000000000 --- a/spec/unit/parser/functions/template_spec_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the template function" do - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should exist" do - Puppet::Parser::Functions.function("template").should == "function_template" - end - - it "should create a TemplateWrapper when called" do - tw = stub_everything 'template_wrapper' - - Puppet::Parser::TemplateWrapper.expects(:new).returns(tw) - - @scope.function_template("test") - end - - it "should give the template filename to the TemplateWrapper" do - tw = stub_everything 'template_wrapper' - Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) - - tw.expects(:file=).with("test") - - @scope.function_template("test") - end - - it "should return what TemplateWrapper.result returns" do - tw = stub_everything 'template_wrapper' - Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) - tw.stubs(:file=).with("test") - - tw.expects(:result).returns("template contents evaluated") - - @scope.function_template("test").should == "template contents evaluated" - end - - it "should concatenate template wrapper outputs for multiple templates" do - tw1 = stub_everything "template_wrapper1" - tw2 = stub_everything "template_wrapper2" - Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw1,tw2) - tw1.stubs(:file=).with("1") - tw2.stubs(:file=).with("2") - tw1.stubs(:result).returns("result1") - tw2.stubs(:result).returns("result2") - - @scope.function_template(["1","2"]).should == "result1result2" - end - - it "should raise an error if the template raises an error" do - tw = stub_everything 'template_wrapper' - Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) - tw.stubs(:result).raises - - lambda { @scope.function_template("1") }.should raise_error(Puppet::ParseError) - end - -end \ No newline at end of file diff --git a/spec/unit/parser/functions/versioncmp_spec.rb b/spec/unit/parser/functions/versioncmp_spec.rb new file mode 100755 index 000000000..0595f8711 --- /dev/null +++ b/spec/unit/parser/functions/versioncmp_spec.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "the versioncmp function" do + + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should exist" do + Puppet::Parser::Functions.function("versioncmp").should == "function_versioncmp" + end + + it "should raise a ParseError if there is less than 2 arguments" do + lambda { @scope.function_versioncmp(["1.2"]) }.should raise_error(Puppet::ParseError) + end + + it "should raise a ParseError if there is more than 2 arguments" do + lambda { @scope.function_versioncmp(["1.2", "2.4.5", "3.5.6"]) }.should raise_error(Puppet::ParseError) + end + + it "should call Puppet::Util::Package.versioncmp (included in scope)" do + Puppet::Util::Package.expects(:versioncmp).with("1.2", "1.3").returns(-1) + + @scope.function_versioncmp(["1.2", "1.3"]) + end + +end diff --git a/spec/unit/parser/functions/versioncmp_spec_spec.rb b/spec/unit/parser/functions/versioncmp_spec_spec.rb deleted file mode 100755 index 0595f8711..000000000 --- a/spec/unit/parser/functions/versioncmp_spec_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe "the versioncmp function" do - - before :each do - @scope = Puppet::Parser::Scope.new() - end - - it "should exist" do - Puppet::Parser::Functions.function("versioncmp").should == "function_versioncmp" - end - - it "should raise a ParseError if there is less than 2 arguments" do - lambda { @scope.function_versioncmp(["1.2"]) }.should raise_error(Puppet::ParseError) - end - - it "should raise a ParseError if there is more than 2 arguments" do - lambda { @scope.function_versioncmp(["1.2", "2.4.5", "3.5.6"]) }.should raise_error(Puppet::ParseError) - end - - it "should call Puppet::Util::Package.versioncmp (included in scope)" do - Puppet::Util::Package.expects(:versioncmp).with("1.2", "1.3").returns(-1) - - @scope.function_versioncmp(["1.2", "1.3"]) - end - -end diff --git a/spec/unit/parser/functions_spec.rb b/spec/unit/parser/functions_spec.rb new file mode 100644 index 000000000..f605052b5 --- /dev/null +++ b/spec/unit/parser/functions_spec.rb @@ -0,0 +1,102 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Parser::Functions do + + after(:each) do + # Rationale: + # our various tests will almost all register to Pupet::Parser::Functions + # a new function called "name". All tests are required to stub Puppet::Parser::Scope + # so that +no+ new real ruby method are defined. + # After each test, we want to leave the whole Puppet::Parser::Functions environment + # as it was before we were called, hence we call rmfunction (which might not succeed + # if the function hasn't been registered in the test). It is also important in this + # section to stub +remove_method+ here so that we don't pollute the scope. + Puppet::Parser::Scope.stubs(:remove_method) + begin + Puppet::Parser::Functions.rmfunction("name") + rescue + end + end + + it "should have a method for returning an environment-specific module" do + Puppet::Parser::Functions.environment_module("myenv").should be_instance_of(Module) + end + + it "should use the current default environment if no environment is provided" do + Puppet::Parser::Functions.environment_module().should be_instance_of(Module) + end + + describe "when calling newfunction" do + before do + @module = Module.new + Puppet::Parser::Functions.stubs(:environment_module).returns @module + end + + it "should create the function in the environment module" do + @module.expects(:define_method).with { |name,block| name == "function_name" } + + Puppet::Parser::Functions.newfunction("name", :type => :rvalue) + end + + it "should raise an error if the function already exists" do + @module.expects(:define_method).with { |name,block| name == "function_name" }.once + Puppet::Parser::Functions.newfunction("name", :type => :rvalue) + + lambda { Puppet::Parser::Functions.newfunction("name", :type => :rvalue) }.should raise_error + end + + it "should raise an error if the function type is not correct" do + @module.expects(:define_method).with { |name,block| name == "function_name" }.never + + lambda { Puppet::Parser::Functions.newfunction("name", :type => :unknown) }.should raise_error + end + end + + describe "when calling rmfunction" do + before do + @module = Module.new + Puppet::Parser::Functions.stubs(:environment_module).returns @module + end + + it "should remove the function in the scope class" do + @module.expects(:define_method).with { |name,block| name == "function_name" } + Puppet::Parser::Functions.newfunction("name", :type => :rvalue) + + @module.expects(:remove_method).with("function_name").once + + Puppet::Parser::Functions.rmfunction("name") + end + + it "should raise an error if the function doesn't exists" do + lambda { Puppet::Parser::Functions.rmfunction("name") }.should raise_error + end + end + + describe "when calling function to test function existance" do + before do + @module = Module.new + Puppet::Parser::Functions.stubs(:environment_module).returns @module + end + + it "should return false if the function doesn't exist" do + Puppet::Parser::Functions.autoloader.stubs(:load) + + Puppet::Parser::Functions.function("name").should be_false + end + + it "should return its name if the function exists" do + @module.expects(:define_method).with { |name,block| name == "function_name" } + Puppet::Parser::Functions.newfunction("name", :type => :rvalue) + + Puppet::Parser::Functions.function("name").should == "function_name" + end + + it "should try to autoload the function if it doesn't exist yet" do + Puppet::Parser::Functions.autoloader.expects(:load) + + Puppet::Parser::Functions.function("name") + end + end +end diff --git a/spec/unit/parser/functions_spec_spec.rb b/spec/unit/parser/functions_spec_spec.rb deleted file mode 100644 index f605052b5..000000000 --- a/spec/unit/parser/functions_spec_spec.rb +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Parser::Functions do - - after(:each) do - # Rationale: - # our various tests will almost all register to Pupet::Parser::Functions - # a new function called "name". All tests are required to stub Puppet::Parser::Scope - # so that +no+ new real ruby method are defined. - # After each test, we want to leave the whole Puppet::Parser::Functions environment - # as it was before we were called, hence we call rmfunction (which might not succeed - # if the function hasn't been registered in the test). It is also important in this - # section to stub +remove_method+ here so that we don't pollute the scope. - Puppet::Parser::Scope.stubs(:remove_method) - begin - Puppet::Parser::Functions.rmfunction("name") - rescue - end - end - - it "should have a method for returning an environment-specific module" do - Puppet::Parser::Functions.environment_module("myenv").should be_instance_of(Module) - end - - it "should use the current default environment if no environment is provided" do - Puppet::Parser::Functions.environment_module().should be_instance_of(Module) - end - - describe "when calling newfunction" do - before do - @module = Module.new - Puppet::Parser::Functions.stubs(:environment_module).returns @module - end - - it "should create the function in the environment module" do - @module.expects(:define_method).with { |name,block| name == "function_name" } - - Puppet::Parser::Functions.newfunction("name", :type => :rvalue) - end - - it "should raise an error if the function already exists" do - @module.expects(:define_method).with { |name,block| name == "function_name" }.once - Puppet::Parser::Functions.newfunction("name", :type => :rvalue) - - lambda { Puppet::Parser::Functions.newfunction("name", :type => :rvalue) }.should raise_error - end - - it "should raise an error if the function type is not correct" do - @module.expects(:define_method).with { |name,block| name == "function_name" }.never - - lambda { Puppet::Parser::Functions.newfunction("name", :type => :unknown) }.should raise_error - end - end - - describe "when calling rmfunction" do - before do - @module = Module.new - Puppet::Parser::Functions.stubs(:environment_module).returns @module - end - - it "should remove the function in the scope class" do - @module.expects(:define_method).with { |name,block| name == "function_name" } - Puppet::Parser::Functions.newfunction("name", :type => :rvalue) - - @module.expects(:remove_method).with("function_name").once - - Puppet::Parser::Functions.rmfunction("name") - end - - it "should raise an error if the function doesn't exists" do - lambda { Puppet::Parser::Functions.rmfunction("name") }.should raise_error - end - end - - describe "when calling function to test function existance" do - before do - @module = Module.new - Puppet::Parser::Functions.stubs(:environment_module).returns @module - end - - it "should return false if the function doesn't exist" do - Puppet::Parser::Functions.autoloader.stubs(:load) - - Puppet::Parser::Functions.function("name").should be_false - end - - it "should return its name if the function exists" do - @module.expects(:define_method).with { |name,block| name == "function_name" } - Puppet::Parser::Functions.newfunction("name", :type => :rvalue) - - Puppet::Parser::Functions.function("name").should == "function_name" - end - - it "should try to autoload the function if it doesn't exist yet" do - Puppet::Parser::Functions.autoloader.expects(:load) - - Puppet::Parser::Functions.function("name") - end - end -end diff --git a/spec/unit/parser/lexer_spec.rb b/spec/unit/parser/lexer_spec.rb new file mode 100755 index 000000000..c437034a4 --- /dev/null +++ b/spec/unit/parser/lexer_spec.rb @@ -0,0 +1,640 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/parser/lexer' + +# This is a special matcher to match easily lexer output +Spec::Matchers.define :be_like do |*expected| + match do |actual| + expected.zip(actual).all? { |e,a| !e or a[0] == e or (e.is_a? Array and a[0] == e[0] and (a[1] == e[1] or (a[1].is_a?(Hash) and a[1][:value] == e[1]))) } + end +end +__ = nil + +describe Puppet::Parser::Lexer do + describe "when reading strings" do + before { @lexer = Puppet::Parser::Lexer.new } + it "should increment the line count for every carriage return in the string" do + @lexer.line = 10 + @lexer.string = "this\nis\natest'" + @lexer.slurpstring("'") + + @lexer.line.should == 12 + end + + it "should not increment the line count for escapes in the string" do + @lexer.line = 10 + @lexer.string = "this\\nis\\natest'" + @lexer.slurpstring("'") + + @lexer.line.should == 10 + end + end +end + +describe Puppet::Parser::Lexer::Token do + before do + @token = Puppet::Parser::Lexer::Token.new(%r{something}, :NAME) + end + + [:regex, :name, :string, :skip, :incr_line, :skip_text, :accumulate].each do |param| + it "should have a #{param.to_s} reader" do + @token.should be_respond_to(param) + end + + it "should have a #{param.to_s} writer" do + @token.should be_respond_to(param.to_s + "=") + end + end +end + +describe Puppet::Parser::Lexer::Token, "when initializing" do + it "should create a regex if the first argument is a string" do + Puppet::Parser::Lexer::Token.new("something", :NAME).regex.should == %r{something} + end + + it "should set the string if the first argument is one" do + Puppet::Parser::Lexer::Token.new("something", :NAME).string.should == "something" + end + + it "should set the regex if the first argument is one" do + Puppet::Parser::Lexer::Token.new(%r{something}, :NAME).regex.should == %r{something} + end +end + +describe Puppet::Parser::Lexer::TokenList do + before do + @list = Puppet::Parser::Lexer::TokenList.new + end + + it "should have a method for retrieving tokens by the name" do + token = @list.add_token :name, "whatever" + @list[:name].should equal(token) + end + + it "should have a method for retrieving string tokens by the string" do + token = @list.add_token :name, "whatever" + @list.lookup("whatever").should equal(token) + end + + it "should add tokens to the list when directed" do + token = @list.add_token :name, "whatever" + @list[:name].should equal(token) + end + + it "should have a method for adding multiple tokens at once" do + @list.add_tokens "whatever" => :name, "foo" => :bar + @list[:name].should_not be_nil + @list[:bar].should_not be_nil + end + + it "should fail to add tokens sharing a name with an existing token" do + @list.add_token :name, "whatever" + lambda { @list.add_token :name, "whatever" }.should raise_error(ArgumentError) + end + + it "should set provided options on tokens being added" do + token = @list.add_token :name, "whatever", :skip_text => true + token.skip_text.should == true + end + + it "should define any provided blocks as a :convert method" do + token = @list.add_token(:name, "whatever") do "foo" end + token.convert.should == "foo" + end + + it "should store all string tokens in the :string_tokens list" do + one = @list.add_token(:name, "1") + @list.string_tokens.should be_include(one) + end + + it "should store all regex tokens in the :regex_tokens list" do + one = @list.add_token(:name, %r{one}) + @list.regex_tokens.should be_include(one) + end + + it "should not store string tokens in the :regex_tokens list" do + one = @list.add_token(:name, "1") + @list.regex_tokens.should_not be_include(one) + end + + it "should not store regex tokens in the :string_tokens list" do + one = @list.add_token(:name, %r{one}) + @list.string_tokens.should_not be_include(one) + end + + it "should sort the string tokens inversely by length when asked" do + one = @list.add_token(:name, "1") + two = @list.add_token(:other, "12") + @list.sort_tokens + @list.string_tokens.should == [two, one] + end +end + +describe Puppet::Parser::Lexer::TOKENS do + before do + @lexer = Puppet::Parser::Lexer.new() + end + + { + :LBRACK => '[', + :RBRACK => ']', + :LBRACE => '{', + :RBRACE => '}', + :LPAREN => '(', + :RPAREN => ')', + :EQUALS => '=', + :ISEQUAL => '==', + :GREATEREQUAL => '>=', + :GREATERTHAN => '>', + :LESSTHAN => '<', + :LESSEQUAL => '<=', + :NOTEQUAL => '!=', + :NOT => '!', + :COMMA => ',', + :DOT => '.', + :COLON => ':', + :AT => '@', + :LLCOLLECT => '<<|', + :RRCOLLECT => '|>>', + :LCOLLECT => '<|', + :RCOLLECT => '|>', + :SEMIC => ';', + :QMARK => '?', + :BACKSLASH => '\\', + :FARROW => '=>', + :PARROW => '+>', + :APPENDS => '+=', + :PLUS => '+', + :MINUS => '-', + :DIV => '/', + :TIMES => '*', + :LSHIFT => '<<', + :RSHIFT => '>>', + :MATCH => '=~', + :NOMATCH => '!~', + :IN_EDGE => '->', + :OUT_EDGE => '<-', + :IN_EDGE_SUB => '~>', + :OUT_EDGE_SUB => '<~', + }.each do |name, string| + it "should have a token named #{name.to_s}" do + Puppet::Parser::Lexer::TOKENS[name].should_not be_nil + end + + it "should match '#{string}' for the token #{name.to_s}" do + Puppet::Parser::Lexer::TOKENS[name].string.should == string + end + end + + { + "case" => :CASE, + "class" => :CLASS, + "default" => :DEFAULT, + "define" => :DEFINE, + "import" => :IMPORT, + "if" => :IF, + "elsif" => :ELSIF, + "else" => :ELSE, + "inherits" => :INHERITS, + "node" => :NODE, + "and" => :AND, + "or" => :OR, + "undef" => :UNDEF, + "false" => :FALSE, + "true" => :TRUE, + "in" => :IN, + }.each do |string, name| + it "should have a keyword named #{name.to_s}" do + Puppet::Parser::Lexer::KEYWORDS[name].should_not be_nil + end + + it "should have the keyword for #{name.to_s} set to #{string}" do + Puppet::Parser::Lexer::KEYWORDS[name].string.should == string + end + end + + # These tokens' strings don't matter, just that the tokens exist. + [:STRING, :DQPRE, :DQMID, :DQPOST, :BOOLEAN, :NAME, :NUMBER, :COMMENT, :MLCOMMENT, :RETURN, :SQUOTE, :DQUOTE, :VARIABLE].each do |name| + it "should have a token named #{name.to_s}" do + Puppet::Parser::Lexer::TOKENS[name].should_not be_nil + end + end +end + +describe Puppet::Parser::Lexer::TOKENS[:CLASSNAME] do + before { @token = Puppet::Parser::Lexer::TOKENS[:CLASSNAME] } + + it "should match against lower-case alpha-numeric terms separated by double colons" do + @token.regex.should =~ "one::two" + end + + it "should match against many lower-case alpha-numeric terms separated by double colons" do + @token.regex.should =~ "one::two::three::four::five" + end + + it "should match against lower-case alpha-numeric terms prefixed by double colons" do + @token.regex.should =~ "::one" + end +end + +describe Puppet::Parser::Lexer::TOKENS[:CLASSREF] do + before { @token = Puppet::Parser::Lexer::TOKENS[:CLASSREF] } + + it "should match against single upper-case alpha-numeric terms" do + @token.regex.should =~ "One" + end + + it "should match against upper-case alpha-numeric terms separated by double colons" do + @token.regex.should =~ "One::Two" + end + + it "should match against many upper-case alpha-numeric terms separated by double colons" do + @token.regex.should =~ "One::Two::Three::Four::Five" + end + + it "should match against upper-case alpha-numeric terms prefixed by double colons" do + @token.regex.should =~ "::One" + end +end + +describe Puppet::Parser::Lexer::TOKENS[:NAME] do + before { @token = Puppet::Parser::Lexer::TOKENS[:NAME] } + + it "should match against lower-case alpha-numeric terms" do + @token.regex.should =~ "one-two" + end + + it "should return itself and the value if the matched term is not a keyword" do + Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(nil) + @token.convert(stub("lexer"), "myval").should == [Puppet::Parser::Lexer::TOKENS[:NAME], "myval"] + end + + it "should return the keyword token and the value if the matched term is a keyword" do + keyword = stub 'keyword', :name => :testing + Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) + @token.convert(stub("lexer"), "myval").should == [keyword, "myval"] + end + + it "should return the BOOLEAN token and 'true' if the matched term is the string 'true'" do + keyword = stub 'keyword', :name => :TRUE + Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) + @token.convert(stub('lexer'), "true").should == [Puppet::Parser::Lexer::TOKENS[:BOOLEAN], true] + end + + it "should return the BOOLEAN token and 'false' if the matched term is the string 'false'" do + keyword = stub 'keyword', :name => :FALSE + Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) + @token.convert(stub('lexer'), "false").should == [Puppet::Parser::Lexer::TOKENS[:BOOLEAN], false] + end +end + +describe Puppet::Parser::Lexer::TOKENS[:NUMBER] do + before do + @token = Puppet::Parser::Lexer::TOKENS[:NUMBER] + @regex = @token.regex + end + + it "should match against numeric terms" do + @regex.should =~ "2982383139" + end + + it "should match against float terms" do + @regex.should =~ "29823.235" + end + + it "should match against hexadecimal terms" do + @regex.should =~ "0xBEEF0023" + end + + it "should match against float with exponent terms" do + @regex.should =~ "10e23" + end + + it "should match against float terms with negative exponents" do + @regex.should =~ "10e-23" + end + + it "should match against float terms with fractional parts and exponent" do + @regex.should =~ "1.234e23" + end + + it "should return the NAME token and the value" do + @token.convert(stub("lexer"), "myval").should == [Puppet::Parser::Lexer::TOKENS[:NAME], "myval"] + end +end + +describe Puppet::Parser::Lexer::TOKENS[:COMMENT] do + before { @token = Puppet::Parser::Lexer::TOKENS[:COMMENT] } + + it "should match against lines starting with '#'" do + @token.regex.should =~ "# this is a comment" + end + + it "should be marked to get skipped" do + @token.skip?.should be_true + end + + it "should be marked to accumulate" do + @token.accumulate?.should be_true + end + + it "'s block should return the comment without the #" do + @token.convert(@lexer,"# this is a comment")[1].should == "this is a comment" + end +end + +describe Puppet::Parser::Lexer::TOKENS[:MLCOMMENT] do + before do + @token = Puppet::Parser::Lexer::TOKENS[:MLCOMMENT] + @lexer = stub 'lexer', :line => 0 + end + + it "should match against lines enclosed with '/*' and '*/'" do + @token.regex.should =~ "/* this is a comment */" + end + + it "should match multiple lines enclosed with '/*' and '*/'" do + @token.regex.should =~ """/* + this is a comment + */""" + end + + it "should increase the lexer current line number by the amount of lines spanned by the comment" do + @lexer.expects(:line=).with(2) + @token.convert(@lexer, "1\n2\n3") + end + + it "should not greedily match comments" do + match = @token.regex.match("/* first */ word /* second */") + match[1].should == " first " + end + + it "should be marked to accumulate" do + @token.accumulate?.should be_true + end + + it "'s block should return the comment without the comment marks" do + @lexer.stubs(:line=).with(0) + + @token.convert(@lexer,"/* this is a comment */")[1].should == "this is a comment" + end + +end + +describe Puppet::Parser::Lexer::TOKENS[:RETURN] do + before { @token = Puppet::Parser::Lexer::TOKENS[:RETURN] } + + it "should match against carriage returns" do + @token.regex.should =~ "\n" + end + + it "should be marked to initiate text skipping" do + @token.skip_text.should be_true + end + + it "should be marked to increment the line" do + @token.incr_line.should be_true + end +end + +def tokens_scanned_from(s) + lexer = Puppet::Parser::Lexer.new + lexer.string = s + lexer.fullscan[0..-2] +end + +describe Puppet::Parser::Lexer,"when lexing strings" do + { + %q['single quoted string')] => [[:STRING,'single quoted string']], + %q["double quoted string"] => [[:STRING,'double quoted string']], + %q['single quoted string with an escaped "\\'"'] => [[:STRING,'single quoted string with an escaped "\'"']], + %q["string with an escaped '\\"'"] => [[:STRING,"string with an escaped '\"'"]], + %q["string with an escaped '\\$'"] => [[:STRING,"string with an escaped '$'"]], + %q["string with $v (but no braces)"] => [[:DQPRE,"string with "],[:VARIABLE,'v'],[:DQPOST,' (but no braces)']], + %q["string with ${v} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,'v'],[:DQPOST,' in braces']], + %q["string with ${qualified::var} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,'qualified::var'],[:DQPOST,' in braces']], + %q["string with $v and $v (but no braces)"] => [[:DQPRE,"string with "],[:VARIABLE,"v"],[:DQMID," and "],[:VARIABLE,"v"],[:DQPOST," (but no braces)"]], + %q["string with ${v} and ${v} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,"v"],[:DQMID," and "],[:VARIABLE,"v"],[:DQPOST," in braces"]], + %q["string with ${'a nested single quoted string'} inside it."] => [[:DQPRE,"string with "],[:STRING,'a nested single quoted string'],[:DQPOST,' inside it.']], + %q["string with ${['an array ',$v2]} in it."] => [[:DQPRE,"string with "],:LBRACK,[:STRING,"an array "],:COMMA,[:VARIABLE,"v2"],:RBRACK,[:DQPOST," in it."]], + %q{a simple "scanner" test} => [[:NAME,"a"],[:NAME,"simple"], [:STRING,"scanner"],[:NAME,"test"]], + %q{a simple 'single quote scanner' test} => [[:NAME,"a"],[:NAME,"simple"], [:STRING,"single quote scanner"],[:NAME,"test"]], + %q{a harder 'a $b \c"'} => [[:NAME,"a"],[:NAME,"harder"], [:STRING,'a $b \c"']], + %q{a harder "scanner test"} => [[:NAME,"a"],[:NAME,"harder"], [:STRING,"scanner test"]], + %q{a hardest "scanner \"test\""} => [[:NAME,"a"],[:NAME,"hardest"],[:STRING,'scanner "test"']], + %Q{a hardestest "scanner \\"test\\"\n"} => [[:NAME,"a"],[:NAME,"hardestest"],[:STRING,%Q{scanner "test"\n}]], + %q{function("call")} => [[:NAME,"function"],[:LPAREN,"("],[:STRING,'call'],[:RPAREN,")"]], + %q["string with ${(3+5)/4} nested math."] => [[:DQPRE,"string with "],:LPAREN,[:NAME,"3"],:PLUS,[:NAME,"5"],:RPAREN,:DIV,[:NAME,"4"],[:DQPOST," nested math."]] + }.each { |src,expected_result| + it "should handle #{src} correctly" do + tokens_scanned_from(src).should be_like(*expected_result) + end + } +end + +describe Puppet::Parser::Lexer::TOKENS[:DOLLAR_VAR] do + before { @token = Puppet::Parser::Lexer::TOKENS[:DOLLAR_VAR] } + + it "should match against alpha words prefixed with '$'" do + @token.regex.should =~ '$this_var' + end + + it "should return the VARIABLE token and the variable name stripped of the '$'" do + @token.convert(stub("lexer"), "$myval").should == [Puppet::Parser::Lexer::TOKENS[:VARIABLE], "myval"] + end +end + +describe Puppet::Parser::Lexer::TOKENS[:REGEX] do + before { @token = Puppet::Parser::Lexer::TOKENS[:REGEX] } + + it "should match against any expression enclosed in //" do + @token.regex.should =~ '/this is a regex/' + end + + it 'should not match if there is \n in the regex' do + @token.regex.should_not =~ "/this is \n a regex/" + end + + describe "when scanning" do + it "should not consider escaped slashes to be the end of a regex" do + tokens_scanned_from("$x =~ /this \\/ foo/").should be_like(__,__,[:REGEX,%r{this / foo}]) + end + + it "should not lex chained division as a regex" do + tokens_scanned_from("$x = $a/$b/$c").collect { |name, data| name }.should_not be_include( :REGEX ) + end + + it "should accept a regular expression after NODE" do + tokens_scanned_from("node /www.*\.mysite\.org/").should be_like(__,[:REGEX,Regexp.new("www.*\.mysite\.org")]) + end + + it "should accept regular expressions in a CASE" do + s = %q{case $variable { + "something": {$othervar = 4096 / 2} + /regex/: {notice("this notably sucks")} + } + } + tokens_scanned_from(s).should be_like( + :CASE,:VARIABLE,:LBRACE,:STRING,:COLON,:LBRACE,:VARIABLE,:EQUALS,:NAME,:DIV,:NAME,:RBRACE,[:REGEX,/regex/],:COLON,:LBRACE,:NAME,:LPAREN,:STRING,:RPAREN,:RBRACE,:RBRACE + ) + end + + end + + + it "should return the REGEX token and a Regexp" do + @token.convert(stub("lexer"), "/myregex/").should == [Puppet::Parser::Lexer::TOKENS[:REGEX], Regexp.new(/myregex/)] + end +end + +describe Puppet::Parser::Lexer, "when lexing comments" do + before { @lexer = Puppet::Parser::Lexer.new } + + it "should accumulate token in munge_token" do + token = stub 'token', :skip => true, :accumulate? => true, :incr_line => nil, :skip_text => false + + token.stubs(:convert).with(@lexer, "# this is a comment").returns([token, " this is a comment"]) + @lexer.munge_token(token, "# this is a comment") + @lexer.munge_token(token, "# this is a comment") + + @lexer.getcomment.should == " this is a comment\n this is a comment\n" + end + + it "should add a new comment stack level on LBRACE" do + @lexer.string = "{" + + @lexer.expects(:commentpush) + + @lexer.fullscan + end + + it "should return the current comments on getcomment" do + @lexer.string = "# comment" + @lexer.fullscan + + @lexer.getcomment.should == "comment\n" + end + + it "should discard the previous comments on blank line" do + @lexer.string = "# 1\n\n# 2" + @lexer.fullscan + + @lexer.getcomment.should == "2\n" + end + + it "should skip whitespace before lexing the next token after a non-token" do + tokens_scanned_from("/* 1\n\n */ \ntest").should be_like([:NAME, "test"]) + end + + it "should not return comments seen after the current line" do + @lexer.string = "# 1\n\n# 2" + @lexer.fullscan + + @lexer.getcomment(1).should == "" + end + + it "should return a comment seen before the current line" do + @lexer.string = "# 1\n# 2" + @lexer.fullscan + + @lexer.getcomment(2).should == "1\n2\n" + end +end + +# FIXME: We need to rewrite all of these tests, but I just don't want to take the time right now. +describe "Puppet::Parser::Lexer in the old tests" do + before { @lexer = Puppet::Parser::Lexer.new } + + it "should do simple lexing" do + { + %q{\\} => [[:BACKSLASH,"\\"]], + %q{simplest scanner test} => [[:NAME,"simplest"],[:NAME,"scanner"],[:NAME,"test"]], + %Q{returned scanner test\n} => [[:NAME,"returned"],[:NAME,"scanner"],[:NAME,"test"]] + }.each { |source,expected| + tokens_scanned_from(source).should be_like(*expected) + } + end + + it "should fail usefully" do + lambda { tokens_scanned_from('^') }.should raise_error(RuntimeError) + end + + it "should fail if the string is not set" do + lambda { @lexer.fullscan() }.should raise_error(Puppet::LexError) + end + + it "should correctly identify keywords" do + tokens_scanned_from("case").should be_like([:CASE, "case"]) + end + + it "should correctly parse class references" do + %w{Many Different Words A Word}.each { |t| tokens_scanned_from(t).should be_like([:CLASSREF,t])} + end + + # #774 + it "should correctly parse namespaced class refernces token" do + %w{Foo ::Foo Foo::Bar ::Foo::Bar}.each { |t| tokens_scanned_from(t).should be_like([:CLASSREF, t]) } + end + + it "should correctly parse names" do + %w{this is a bunch of names}.each { |t| tokens_scanned_from(t).should be_like([:NAME,t]) } + end + + it "should correctly parse names with numerals" do + %w{1name name1 11names names11}.each { |t| tokens_scanned_from(t).should be_like([:NAME,t]) } + end + + it "should correctly parse empty strings" do + lambda { tokens_scanned_from('$var = ""') }.should_not raise_error + end + + it "should correctly parse virtual resources" do + tokens_scanned_from("@type {").should be_like([:AT, "@"], [:NAME, "type"], [:LBRACE, "{"]) + end + + it "should correctly deal with namespaces" do + @lexer.string = %{class myclass} + @lexer.fullscan + @lexer.namespace.should == "myclass" + + @lexer.namepop + @lexer.namespace.should == "" + + @lexer.string = "class base { class sub { class more" + @lexer.fullscan + @lexer.namespace.should == "base::sub::more" + + @lexer.namepop + @lexer.namespace.should == "base::sub" + end + + it "should correctly handle fully qualified names" do + @lexer.string = "class base { class sub::more {" + @lexer.fullscan + @lexer.namespace.should == "base::sub::more" + + @lexer.namepop + @lexer.namespace.should == "base" + end + + it "should correctly lex variables" do + ["$variable", "$::variable", "$qualified::variable", "$further::qualified::variable"].each do |string| + tokens_scanned_from(string).should be_like([:VARIABLE,string.sub(/^\$/,'')]) + end + end +end + +require 'puppettest/support/utils' +describe "Puppet::Parser::Lexer in the old tests when lexing example files" do + extend PuppetTest + extend PuppetTest::Support::Utils + textfiles() do |file| + it "should correctly lex #{file}" do + lexer = Puppet::Parser::Lexer.new() + lexer.file = file + lambda { lexer.fullscan() }.should_not raise_error + end + end +end diff --git a/spec/unit/parser/lexer_spec_spec.rb b/spec/unit/parser/lexer_spec_spec.rb deleted file mode 100755 index c437034a4..000000000 --- a/spec/unit/parser/lexer_spec_spec.rb +++ /dev/null @@ -1,640 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/parser/lexer' - -# This is a special matcher to match easily lexer output -Spec::Matchers.define :be_like do |*expected| - match do |actual| - expected.zip(actual).all? { |e,a| !e or a[0] == e or (e.is_a? Array and a[0] == e[0] and (a[1] == e[1] or (a[1].is_a?(Hash) and a[1][:value] == e[1]))) } - end -end -__ = nil - -describe Puppet::Parser::Lexer do - describe "when reading strings" do - before { @lexer = Puppet::Parser::Lexer.new } - it "should increment the line count for every carriage return in the string" do - @lexer.line = 10 - @lexer.string = "this\nis\natest'" - @lexer.slurpstring("'") - - @lexer.line.should == 12 - end - - it "should not increment the line count for escapes in the string" do - @lexer.line = 10 - @lexer.string = "this\\nis\\natest'" - @lexer.slurpstring("'") - - @lexer.line.should == 10 - end - end -end - -describe Puppet::Parser::Lexer::Token do - before do - @token = Puppet::Parser::Lexer::Token.new(%r{something}, :NAME) - end - - [:regex, :name, :string, :skip, :incr_line, :skip_text, :accumulate].each do |param| - it "should have a #{param.to_s} reader" do - @token.should be_respond_to(param) - end - - it "should have a #{param.to_s} writer" do - @token.should be_respond_to(param.to_s + "=") - end - end -end - -describe Puppet::Parser::Lexer::Token, "when initializing" do - it "should create a regex if the first argument is a string" do - Puppet::Parser::Lexer::Token.new("something", :NAME).regex.should == %r{something} - end - - it "should set the string if the first argument is one" do - Puppet::Parser::Lexer::Token.new("something", :NAME).string.should == "something" - end - - it "should set the regex if the first argument is one" do - Puppet::Parser::Lexer::Token.new(%r{something}, :NAME).regex.should == %r{something} - end -end - -describe Puppet::Parser::Lexer::TokenList do - before do - @list = Puppet::Parser::Lexer::TokenList.new - end - - it "should have a method for retrieving tokens by the name" do - token = @list.add_token :name, "whatever" - @list[:name].should equal(token) - end - - it "should have a method for retrieving string tokens by the string" do - token = @list.add_token :name, "whatever" - @list.lookup("whatever").should equal(token) - end - - it "should add tokens to the list when directed" do - token = @list.add_token :name, "whatever" - @list[:name].should equal(token) - end - - it "should have a method for adding multiple tokens at once" do - @list.add_tokens "whatever" => :name, "foo" => :bar - @list[:name].should_not be_nil - @list[:bar].should_not be_nil - end - - it "should fail to add tokens sharing a name with an existing token" do - @list.add_token :name, "whatever" - lambda { @list.add_token :name, "whatever" }.should raise_error(ArgumentError) - end - - it "should set provided options on tokens being added" do - token = @list.add_token :name, "whatever", :skip_text => true - token.skip_text.should == true - end - - it "should define any provided blocks as a :convert method" do - token = @list.add_token(:name, "whatever") do "foo" end - token.convert.should == "foo" - end - - it "should store all string tokens in the :string_tokens list" do - one = @list.add_token(:name, "1") - @list.string_tokens.should be_include(one) - end - - it "should store all regex tokens in the :regex_tokens list" do - one = @list.add_token(:name, %r{one}) - @list.regex_tokens.should be_include(one) - end - - it "should not store string tokens in the :regex_tokens list" do - one = @list.add_token(:name, "1") - @list.regex_tokens.should_not be_include(one) - end - - it "should not store regex tokens in the :string_tokens list" do - one = @list.add_token(:name, %r{one}) - @list.string_tokens.should_not be_include(one) - end - - it "should sort the string tokens inversely by length when asked" do - one = @list.add_token(:name, "1") - two = @list.add_token(:other, "12") - @list.sort_tokens - @list.string_tokens.should == [two, one] - end -end - -describe Puppet::Parser::Lexer::TOKENS do - before do - @lexer = Puppet::Parser::Lexer.new() - end - - { - :LBRACK => '[', - :RBRACK => ']', - :LBRACE => '{', - :RBRACE => '}', - :LPAREN => '(', - :RPAREN => ')', - :EQUALS => '=', - :ISEQUAL => '==', - :GREATEREQUAL => '>=', - :GREATERTHAN => '>', - :LESSTHAN => '<', - :LESSEQUAL => '<=', - :NOTEQUAL => '!=', - :NOT => '!', - :COMMA => ',', - :DOT => '.', - :COLON => ':', - :AT => '@', - :LLCOLLECT => '<<|', - :RRCOLLECT => '|>>', - :LCOLLECT => '<|', - :RCOLLECT => '|>', - :SEMIC => ';', - :QMARK => '?', - :BACKSLASH => '\\', - :FARROW => '=>', - :PARROW => '+>', - :APPENDS => '+=', - :PLUS => '+', - :MINUS => '-', - :DIV => '/', - :TIMES => '*', - :LSHIFT => '<<', - :RSHIFT => '>>', - :MATCH => '=~', - :NOMATCH => '!~', - :IN_EDGE => '->', - :OUT_EDGE => '<-', - :IN_EDGE_SUB => '~>', - :OUT_EDGE_SUB => '<~', - }.each do |name, string| - it "should have a token named #{name.to_s}" do - Puppet::Parser::Lexer::TOKENS[name].should_not be_nil - end - - it "should match '#{string}' for the token #{name.to_s}" do - Puppet::Parser::Lexer::TOKENS[name].string.should == string - end - end - - { - "case" => :CASE, - "class" => :CLASS, - "default" => :DEFAULT, - "define" => :DEFINE, - "import" => :IMPORT, - "if" => :IF, - "elsif" => :ELSIF, - "else" => :ELSE, - "inherits" => :INHERITS, - "node" => :NODE, - "and" => :AND, - "or" => :OR, - "undef" => :UNDEF, - "false" => :FALSE, - "true" => :TRUE, - "in" => :IN, - }.each do |string, name| - it "should have a keyword named #{name.to_s}" do - Puppet::Parser::Lexer::KEYWORDS[name].should_not be_nil - end - - it "should have the keyword for #{name.to_s} set to #{string}" do - Puppet::Parser::Lexer::KEYWORDS[name].string.should == string - end - end - - # These tokens' strings don't matter, just that the tokens exist. - [:STRING, :DQPRE, :DQMID, :DQPOST, :BOOLEAN, :NAME, :NUMBER, :COMMENT, :MLCOMMENT, :RETURN, :SQUOTE, :DQUOTE, :VARIABLE].each do |name| - it "should have a token named #{name.to_s}" do - Puppet::Parser::Lexer::TOKENS[name].should_not be_nil - end - end -end - -describe Puppet::Parser::Lexer::TOKENS[:CLASSNAME] do - before { @token = Puppet::Parser::Lexer::TOKENS[:CLASSNAME] } - - it "should match against lower-case alpha-numeric terms separated by double colons" do - @token.regex.should =~ "one::two" - end - - it "should match against many lower-case alpha-numeric terms separated by double colons" do - @token.regex.should =~ "one::two::three::four::five" - end - - it "should match against lower-case alpha-numeric terms prefixed by double colons" do - @token.regex.should =~ "::one" - end -end - -describe Puppet::Parser::Lexer::TOKENS[:CLASSREF] do - before { @token = Puppet::Parser::Lexer::TOKENS[:CLASSREF] } - - it "should match against single upper-case alpha-numeric terms" do - @token.regex.should =~ "One" - end - - it "should match against upper-case alpha-numeric terms separated by double colons" do - @token.regex.should =~ "One::Two" - end - - it "should match against many upper-case alpha-numeric terms separated by double colons" do - @token.regex.should =~ "One::Two::Three::Four::Five" - end - - it "should match against upper-case alpha-numeric terms prefixed by double colons" do - @token.regex.should =~ "::One" - end -end - -describe Puppet::Parser::Lexer::TOKENS[:NAME] do - before { @token = Puppet::Parser::Lexer::TOKENS[:NAME] } - - it "should match against lower-case alpha-numeric terms" do - @token.regex.should =~ "one-two" - end - - it "should return itself and the value if the matched term is not a keyword" do - Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(nil) - @token.convert(stub("lexer"), "myval").should == [Puppet::Parser::Lexer::TOKENS[:NAME], "myval"] - end - - it "should return the keyword token and the value if the matched term is a keyword" do - keyword = stub 'keyword', :name => :testing - Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) - @token.convert(stub("lexer"), "myval").should == [keyword, "myval"] - end - - it "should return the BOOLEAN token and 'true' if the matched term is the string 'true'" do - keyword = stub 'keyword', :name => :TRUE - Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) - @token.convert(stub('lexer'), "true").should == [Puppet::Parser::Lexer::TOKENS[:BOOLEAN], true] - end - - it "should return the BOOLEAN token and 'false' if the matched term is the string 'false'" do - keyword = stub 'keyword', :name => :FALSE - Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) - @token.convert(stub('lexer'), "false").should == [Puppet::Parser::Lexer::TOKENS[:BOOLEAN], false] - end -end - -describe Puppet::Parser::Lexer::TOKENS[:NUMBER] do - before do - @token = Puppet::Parser::Lexer::TOKENS[:NUMBER] - @regex = @token.regex - end - - it "should match against numeric terms" do - @regex.should =~ "2982383139" - end - - it "should match against float terms" do - @regex.should =~ "29823.235" - end - - it "should match against hexadecimal terms" do - @regex.should =~ "0xBEEF0023" - end - - it "should match against float with exponent terms" do - @regex.should =~ "10e23" - end - - it "should match against float terms with negative exponents" do - @regex.should =~ "10e-23" - end - - it "should match against float terms with fractional parts and exponent" do - @regex.should =~ "1.234e23" - end - - it "should return the NAME token and the value" do - @token.convert(stub("lexer"), "myval").should == [Puppet::Parser::Lexer::TOKENS[:NAME], "myval"] - end -end - -describe Puppet::Parser::Lexer::TOKENS[:COMMENT] do - before { @token = Puppet::Parser::Lexer::TOKENS[:COMMENT] } - - it "should match against lines starting with '#'" do - @token.regex.should =~ "# this is a comment" - end - - it "should be marked to get skipped" do - @token.skip?.should be_true - end - - it "should be marked to accumulate" do - @token.accumulate?.should be_true - end - - it "'s block should return the comment without the #" do - @token.convert(@lexer,"# this is a comment")[1].should == "this is a comment" - end -end - -describe Puppet::Parser::Lexer::TOKENS[:MLCOMMENT] do - before do - @token = Puppet::Parser::Lexer::TOKENS[:MLCOMMENT] - @lexer = stub 'lexer', :line => 0 - end - - it "should match against lines enclosed with '/*' and '*/'" do - @token.regex.should =~ "/* this is a comment */" - end - - it "should match multiple lines enclosed with '/*' and '*/'" do - @token.regex.should =~ """/* - this is a comment - */""" - end - - it "should increase the lexer current line number by the amount of lines spanned by the comment" do - @lexer.expects(:line=).with(2) - @token.convert(@lexer, "1\n2\n3") - end - - it "should not greedily match comments" do - match = @token.regex.match("/* first */ word /* second */") - match[1].should == " first " - end - - it "should be marked to accumulate" do - @token.accumulate?.should be_true - end - - it "'s block should return the comment without the comment marks" do - @lexer.stubs(:line=).with(0) - - @token.convert(@lexer,"/* this is a comment */")[1].should == "this is a comment" - end - -end - -describe Puppet::Parser::Lexer::TOKENS[:RETURN] do - before { @token = Puppet::Parser::Lexer::TOKENS[:RETURN] } - - it "should match against carriage returns" do - @token.regex.should =~ "\n" - end - - it "should be marked to initiate text skipping" do - @token.skip_text.should be_true - end - - it "should be marked to increment the line" do - @token.incr_line.should be_true - end -end - -def tokens_scanned_from(s) - lexer = Puppet::Parser::Lexer.new - lexer.string = s - lexer.fullscan[0..-2] -end - -describe Puppet::Parser::Lexer,"when lexing strings" do - { - %q['single quoted string')] => [[:STRING,'single quoted string']], - %q["double quoted string"] => [[:STRING,'double quoted string']], - %q['single quoted string with an escaped "\\'"'] => [[:STRING,'single quoted string with an escaped "\'"']], - %q["string with an escaped '\\"'"] => [[:STRING,"string with an escaped '\"'"]], - %q["string with an escaped '\\$'"] => [[:STRING,"string with an escaped '$'"]], - %q["string with $v (but no braces)"] => [[:DQPRE,"string with "],[:VARIABLE,'v'],[:DQPOST,' (but no braces)']], - %q["string with ${v} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,'v'],[:DQPOST,' in braces']], - %q["string with ${qualified::var} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,'qualified::var'],[:DQPOST,' in braces']], - %q["string with $v and $v (but no braces)"] => [[:DQPRE,"string with "],[:VARIABLE,"v"],[:DQMID," and "],[:VARIABLE,"v"],[:DQPOST," (but no braces)"]], - %q["string with ${v} and ${v} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,"v"],[:DQMID," and "],[:VARIABLE,"v"],[:DQPOST," in braces"]], - %q["string with ${'a nested single quoted string'} inside it."] => [[:DQPRE,"string with "],[:STRING,'a nested single quoted string'],[:DQPOST,' inside it.']], - %q["string with ${['an array ',$v2]} in it."] => [[:DQPRE,"string with "],:LBRACK,[:STRING,"an array "],:COMMA,[:VARIABLE,"v2"],:RBRACK,[:DQPOST," in it."]], - %q{a simple "scanner" test} => [[:NAME,"a"],[:NAME,"simple"], [:STRING,"scanner"],[:NAME,"test"]], - %q{a simple 'single quote scanner' test} => [[:NAME,"a"],[:NAME,"simple"], [:STRING,"single quote scanner"],[:NAME,"test"]], - %q{a harder 'a $b \c"'} => [[:NAME,"a"],[:NAME,"harder"], [:STRING,'a $b \c"']], - %q{a harder "scanner test"} => [[:NAME,"a"],[:NAME,"harder"], [:STRING,"scanner test"]], - %q{a hardest "scanner \"test\""} => [[:NAME,"a"],[:NAME,"hardest"],[:STRING,'scanner "test"']], - %Q{a hardestest "scanner \\"test\\"\n"} => [[:NAME,"a"],[:NAME,"hardestest"],[:STRING,%Q{scanner "test"\n}]], - %q{function("call")} => [[:NAME,"function"],[:LPAREN,"("],[:STRING,'call'],[:RPAREN,")"]], - %q["string with ${(3+5)/4} nested math."] => [[:DQPRE,"string with "],:LPAREN,[:NAME,"3"],:PLUS,[:NAME,"5"],:RPAREN,:DIV,[:NAME,"4"],[:DQPOST," nested math."]] - }.each { |src,expected_result| - it "should handle #{src} correctly" do - tokens_scanned_from(src).should be_like(*expected_result) - end - } -end - -describe Puppet::Parser::Lexer::TOKENS[:DOLLAR_VAR] do - before { @token = Puppet::Parser::Lexer::TOKENS[:DOLLAR_VAR] } - - it "should match against alpha words prefixed with '$'" do - @token.regex.should =~ '$this_var' - end - - it "should return the VARIABLE token and the variable name stripped of the '$'" do - @token.convert(stub("lexer"), "$myval").should == [Puppet::Parser::Lexer::TOKENS[:VARIABLE], "myval"] - end -end - -describe Puppet::Parser::Lexer::TOKENS[:REGEX] do - before { @token = Puppet::Parser::Lexer::TOKENS[:REGEX] } - - it "should match against any expression enclosed in //" do - @token.regex.should =~ '/this is a regex/' - end - - it 'should not match if there is \n in the regex' do - @token.regex.should_not =~ "/this is \n a regex/" - end - - describe "when scanning" do - it "should not consider escaped slashes to be the end of a regex" do - tokens_scanned_from("$x =~ /this \\/ foo/").should be_like(__,__,[:REGEX,%r{this / foo}]) - end - - it "should not lex chained division as a regex" do - tokens_scanned_from("$x = $a/$b/$c").collect { |name, data| name }.should_not be_include( :REGEX ) - end - - it "should accept a regular expression after NODE" do - tokens_scanned_from("node /www.*\.mysite\.org/").should be_like(__,[:REGEX,Regexp.new("www.*\.mysite\.org")]) - end - - it "should accept regular expressions in a CASE" do - s = %q{case $variable { - "something": {$othervar = 4096 / 2} - /regex/: {notice("this notably sucks")} - } - } - tokens_scanned_from(s).should be_like( - :CASE,:VARIABLE,:LBRACE,:STRING,:COLON,:LBRACE,:VARIABLE,:EQUALS,:NAME,:DIV,:NAME,:RBRACE,[:REGEX,/regex/],:COLON,:LBRACE,:NAME,:LPAREN,:STRING,:RPAREN,:RBRACE,:RBRACE - ) - end - - end - - - it "should return the REGEX token and a Regexp" do - @token.convert(stub("lexer"), "/myregex/").should == [Puppet::Parser::Lexer::TOKENS[:REGEX], Regexp.new(/myregex/)] - end -end - -describe Puppet::Parser::Lexer, "when lexing comments" do - before { @lexer = Puppet::Parser::Lexer.new } - - it "should accumulate token in munge_token" do - token = stub 'token', :skip => true, :accumulate? => true, :incr_line => nil, :skip_text => false - - token.stubs(:convert).with(@lexer, "# this is a comment").returns([token, " this is a comment"]) - @lexer.munge_token(token, "# this is a comment") - @lexer.munge_token(token, "# this is a comment") - - @lexer.getcomment.should == " this is a comment\n this is a comment\n" - end - - it "should add a new comment stack level on LBRACE" do - @lexer.string = "{" - - @lexer.expects(:commentpush) - - @lexer.fullscan - end - - it "should return the current comments on getcomment" do - @lexer.string = "# comment" - @lexer.fullscan - - @lexer.getcomment.should == "comment\n" - end - - it "should discard the previous comments on blank line" do - @lexer.string = "# 1\n\n# 2" - @lexer.fullscan - - @lexer.getcomment.should == "2\n" - end - - it "should skip whitespace before lexing the next token after a non-token" do - tokens_scanned_from("/* 1\n\n */ \ntest").should be_like([:NAME, "test"]) - end - - it "should not return comments seen after the current line" do - @lexer.string = "# 1\n\n# 2" - @lexer.fullscan - - @lexer.getcomment(1).should == "" - end - - it "should return a comment seen before the current line" do - @lexer.string = "# 1\n# 2" - @lexer.fullscan - - @lexer.getcomment(2).should == "1\n2\n" - end -end - -# FIXME: We need to rewrite all of these tests, but I just don't want to take the time right now. -describe "Puppet::Parser::Lexer in the old tests" do - before { @lexer = Puppet::Parser::Lexer.new } - - it "should do simple lexing" do - { - %q{\\} => [[:BACKSLASH,"\\"]], - %q{simplest scanner test} => [[:NAME,"simplest"],[:NAME,"scanner"],[:NAME,"test"]], - %Q{returned scanner test\n} => [[:NAME,"returned"],[:NAME,"scanner"],[:NAME,"test"]] - }.each { |source,expected| - tokens_scanned_from(source).should be_like(*expected) - } - end - - it "should fail usefully" do - lambda { tokens_scanned_from('^') }.should raise_error(RuntimeError) - end - - it "should fail if the string is not set" do - lambda { @lexer.fullscan() }.should raise_error(Puppet::LexError) - end - - it "should correctly identify keywords" do - tokens_scanned_from("case").should be_like([:CASE, "case"]) - end - - it "should correctly parse class references" do - %w{Many Different Words A Word}.each { |t| tokens_scanned_from(t).should be_like([:CLASSREF,t])} - end - - # #774 - it "should correctly parse namespaced class refernces token" do - %w{Foo ::Foo Foo::Bar ::Foo::Bar}.each { |t| tokens_scanned_from(t).should be_like([:CLASSREF, t]) } - end - - it "should correctly parse names" do - %w{this is a bunch of names}.each { |t| tokens_scanned_from(t).should be_like([:NAME,t]) } - end - - it "should correctly parse names with numerals" do - %w{1name name1 11names names11}.each { |t| tokens_scanned_from(t).should be_like([:NAME,t]) } - end - - it "should correctly parse empty strings" do - lambda { tokens_scanned_from('$var = ""') }.should_not raise_error - end - - it "should correctly parse virtual resources" do - tokens_scanned_from("@type {").should be_like([:AT, "@"], [:NAME, "type"], [:LBRACE, "{"]) - end - - it "should correctly deal with namespaces" do - @lexer.string = %{class myclass} - @lexer.fullscan - @lexer.namespace.should == "myclass" - - @lexer.namepop - @lexer.namespace.should == "" - - @lexer.string = "class base { class sub { class more" - @lexer.fullscan - @lexer.namespace.should == "base::sub::more" - - @lexer.namepop - @lexer.namespace.should == "base::sub" - end - - it "should correctly handle fully qualified names" do - @lexer.string = "class base { class sub::more {" - @lexer.fullscan - @lexer.namespace.should == "base::sub::more" - - @lexer.namepop - @lexer.namespace.should == "base" - end - - it "should correctly lex variables" do - ["$variable", "$::variable", "$qualified::variable", "$further::qualified::variable"].each do |string| - tokens_scanned_from(string).should be_like([:VARIABLE,string.sub(/^\$/,'')]) - end - end -end - -require 'puppettest/support/utils' -describe "Puppet::Parser::Lexer in the old tests when lexing example files" do - extend PuppetTest - extend PuppetTest::Support::Utils - textfiles() do |file| - it "should correctly lex #{file}" do - lexer = Puppet::Parser::Lexer.new() - lexer.file = file - lambda { lexer.fullscan() }.should_not raise_error - end - end -end diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb new file mode 100755 index 000000000..ed4492a87 --- /dev/null +++ b/spec/unit/parser/parser_spec.rb @@ -0,0 +1,406 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Parser do + + ast = Puppet::Parser::AST + + before :each do + @known_resource_types = Puppet::Resource::TypeCollection.new("development") + @parser = Puppet::Parser::Parser.new "development" + @parser.stubs(:known_resource_types).returns @known_resource_types + @true_ast = Puppet::Parser::AST::Boolean.new :value => true + end + + it "should require an environment at initialization" do + lambda { Puppet::Parser::Parser.new }.should raise_error(ArgumentError) + end + + it "should set the environment" do + env = Puppet::Node::Environment.new + Puppet::Parser::Parser.new(env).environment.should == env + end + + it "should convert the environment into an environment instance if a string is provided" do + env = Puppet::Node::Environment.new("testing") + Puppet::Parser::Parser.new("testing").environment.should == env + end + + it "should be able to look up the environment-specific resource type collection" do + rtc = Puppet::Node::Environment.new("development").known_resource_types + parser = Puppet::Parser::Parser.new "development" + parser.known_resource_types.should equal(rtc) + end + + it "should delegate importing to the known resource type loader" do + parser = Puppet::Parser::Parser.new "development" + parser.known_resource_types.loader.expects(:import).with("newfile", "current_file") + parser.lexer.expects(:file).returns "current_file" + parser.import("newfile") + end + + describe "when parsing files" do + before do + FileTest.stubs(:exist?).returns true + File.stubs(:read).returns "" + @parser.stubs(:watch_file) + end + + it "should treat files ending in 'rb' as ruby files" do + @parser.expects(:parse_ruby_file) + @parser.file = "/my/file.rb" + @parser.parse + end + + describe "in ruby" do + it "should use the ruby interpreter to load the file" do + @parser.file = "/my/file.rb" + @parser.expects(:require).with "/my/file.rb" + + @parser.parse_ruby_file + end + end + end + + describe "when parsing append operator" do + + it "should not raise syntax errors" do + lambda { @parser.parse("$var += something") }.should_not raise_error + end + + it "shouldraise syntax error on incomplete syntax " do + lambda { @parser.parse("$var += ") }.should raise_error + end + + it "should call ast::VarDef with append=true" do + ast::VarDef.expects(:new).with { |h| h[:append] == true } + @parser.parse("$var += 2") + end + + it "should work with arrays too" do + ast::VarDef.expects(:new).with { |h| h[:append] == true } + @parser.parse("$var += ['test']") + end + + end + + describe "when parsing 'if'" do + it "not, it should create the correct ast objects" do + ast::Not.expects(:new).with { |h| h[:value].is_a?(ast::Boolean) } + @parser.parse("if ! true { $var = 1 }") + end + + it "boolean operation, it should create the correct ast objects" do + ast::BooleanOperator.expects(:new).with { + |h| h[:rval].is_a?(ast::Boolean) and h[:lval].is_a?(ast::Boolean) and h[:operator]=="or" + } + @parser.parse("if true or true { $var = 1 }") + + end + + it "comparison operation, it should create the correct ast objects" do + ast::ComparisonOperator.expects(:new).with { + |h| h[:lval].is_a?(ast::Name) and h[:rval].is_a?(ast::Name) and h[:operator]=="<" + } + @parser.parse("if 1 < 2 { $var = 1 }") + + end + + end + + describe "when parsing if complex expressions" do + it "should create a correct ast tree" do + aststub = stub_everything 'ast' + ast::ComparisonOperator.expects(:new).with { + |h| h[:rval].is_a?(ast::Name) and h[:lval].is_a?(ast::Name) and h[:operator]==">" + }.returns(aststub) + ast::ComparisonOperator.expects(:new).with { + |h| h[:rval].is_a?(ast::Name) and h[:lval].is_a?(ast::Name) and h[:operator]=="==" + }.returns(aststub) + ast::BooleanOperator.expects(:new).with { + |h| h[:rval]==aststub and h[:lval]==aststub and h[:operator]=="and" + } + @parser.parse("if (1 > 2) and (1 == 2) { $var = 1 }") + end + + it "should raise an error on incorrect expression" do + lambda { @parser.parse("if (1 > 2 > ) or (1 == 2) { $var = 1 }") }.should raise_error + end + + end + + describe "when parsing resource references" do + + it "should not raise syntax errors" do + lambda { @parser.parse('exec { test: param => File["a"] }') }.should_not raise_error + end + + it "should not raise syntax errors with multiple references" do + lambda { @parser.parse('exec { test: param => File["a","b"] }') }.should_not raise_error + end + + it "should create an ast::ResourceReference" do + ast::Resource.stubs(:new) + ast::ResourceReference.expects(:new).with { |arg| + arg[:line]==1 and arg[:type]=="File" and arg[:title].is_a?(ast::ASTArray) + } + @parser.parse('exec { test: command => File["a","b"] }') + end + end + + describe "when parsing resource overrides" do + + it "should not raise syntax errors" do + lambda { @parser.parse('Resource["title"] { param => value }') }.should_not raise_error + end + + it "should not raise syntax errors with multiple overrides" do + lambda { @parser.parse('Resource["title1","title2"] { param => value }') }.should_not raise_error + end + + it "should create an ast::ResourceOverride" do + ast::ResourceOverride.expects(:new).with { |arg| + arg[:line]==1 and arg[:object].is_a?(ast::ResourceReference) and arg[:parameters].is_a?(ast::ResourceParam) + } + @parser.parse('Resource["title1","title2"] { param => value }') + end + + end + + describe "when parsing if statements" do + + it "should not raise errors with empty if" do + lambda { @parser.parse("if true { }") }.should_not raise_error + end + + it "should not raise errors with empty else" do + lambda { @parser.parse("if false { notice('if') } else { }") }.should_not raise_error + end + + it "should not raise errors with empty if and else" do + lambda { @parser.parse("if false { } else { }") }.should_not raise_error + end + + it "should create a nop node for empty branch" do + ast::Nop.expects(:new) + @parser.parse("if true { }") + end + + it "should create a nop node for empty else branch" do + ast::Nop.expects(:new) + @parser.parse("if true { notice('test') } else { }") + end + + end + + describe "when parsing function calls" do + + it "should not raise errors with no arguments" do + lambda { @parser.parse("tag()") }.should_not raise_error + end + + it "should not raise errors with rvalue function with no args" do + lambda { @parser.parse("$a = template()") }.should_not raise_error + end + + it "should not raise errors with arguments" do + lambda { @parser.parse("notice(1)") }.should_not raise_error + end + + it "should not raise errors with multiple arguments" do + lambda { @parser.parse("notice(1,2)") }.should_not raise_error + end + + it "should not raise errors with multiple arguments and a trailing comma" do + lambda { @parser.parse("notice(1,2,)") }.should_not raise_error + end + + end + + describe "when parsing arrays with trailing comma" do + + it "should not raise errors with a trailing comma" do + lambda { @parser.parse("$a = [1,2,]") }.should_not raise_error + end + end + + describe "when providing AST context" do + before do + @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" + @parser.stubs(:lexer).returns @lexer + end + + it "should include the lexer's line" do + @parser.ast_context[:line].should == 50 + end + + it "should include the lexer's file" do + @parser.ast_context[:file].should == "/foo/bar" + end + + it "should include the docs if directed to do so" do + @parser.ast_context(true)[:doc].should == "whev" + end + + it "should not include the docs when told not to" do + @parser.ast_context(false)[:doc].should be_nil + end + + it "should not include the docs by default" do + @parser.ast_context()[:doc].should be_nil + end + end + + describe "when building ast nodes" do + before do + @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" + @parser.stubs(:lexer).returns @lexer + @class = stub 'class', :use_docs => false + end + + it "should return a new instance of the provided class created with the provided options" do + @class.expects(:new).with { |opts| opts[:foo] == "bar" } + @parser.ast(@class, :foo => "bar") + end + + it "should merge the ast context into the provided options" do + @class.expects(:new).with { |opts| opts[:file] == "/foo" } + @parser.expects(:ast_context).returns :file => "/foo" + @parser.ast(@class, :foo => "bar") + end + + it "should prefer provided options over AST context" do + @class.expects(:new).with { |opts| opts[:file] == "/bar" } + @parser.expects(:ast_context).returns :file => "/foo" + @parser.ast(@class, :file => "/bar") + end + + it "should include docs when the AST class uses them" do + @class.expects(:use_docs).returns true + @class.stubs(:new) + @parser.expects(:ast_context).with(true).returns({}) + @parser.ast(@class, :file => "/bar") + end + end + + describe "when creating a node" do + before :each do + @lexer = stub 'lexer' + @lexer.stubs(:getcomment) + @parser.stubs(:lexer).returns(@lexer) + @node = stub_everything 'node' + @parser.stubs(:ast_context).returns({}) + @parser.stubs(:node).returns(nil) + + @nodename = stub 'nodename', :is_a? => false, :value => "foo" + @nodename.stubs(:is_a?).with(Puppet::Parser::AST::HostName).returns(true) + end + + it "should return an array of nodes" do + @parser.newnode(@nodename).should be_instance_of(Array) + end + end + + describe "when retrieving a specific node" do + it "should delegate to the known_resource_types node" do + @known_resource_types.expects(:node).with("node") + + @parser.node("node") + end + end + + describe "when retrieving a specific class" do + it "should delegate to the loaded code" do + @known_resource_types.expects(:hostclass).with("class") + + @parser.hostclass("class") + end + end + + describe "when retrieving a specific definitions" do + it "should delegate to the loaded code" do + @known_resource_types.expects(:definition).with("define") + + @parser.definition("define") + end + end + + describe "when determining the configuration version" do + it "should determine it from the resource type collection" do + @parser.known_resource_types.expects(:version).returns "foo" + @parser.version.should == "foo" + end + end + + describe "when looking up definitions" do + it "should use the known resource types to check for them by name" do + @parser.known_resource_types.stubs(:find_or_load).with("namespace","name",:definition).returns(:this_value) + @parser.find_definition("namespace","name").should == :this_value + end + end + + describe "when looking up hostclasses" do + it "should use the known resource types to check for them by name" do + @parser.known_resource_types.stubs(:find_or_load).with("namespace","name",:hostclass).returns(:this_value) + @parser.find_hostclass("namespace","name").should == :this_value + end + end + + describe "when parsing classes" do + before :each do + @krt = Puppet::Resource::TypeCollection.new("development") + @parser = Puppet::Parser::Parser.new "development" + @parser.stubs(:known_resource_types).returns @krt + end + + it "should create new classes" do + @parser.parse("class foobar {}") + @krt.hostclass("foobar").should be_instance_of(Puppet::Resource::Type) + end + + it "should correctly set the parent class when one is provided" do + @parser.parse("class foobar inherits yayness {}") + @krt.hostclass("foobar").parent.should == "yayness" + end + + it "should correctly set the parent class for multiple classes at a time" do + @parser.parse("class foobar inherits yayness {}\nclass boo inherits bar {}") + @krt.hostclass("foobar").parent.should == "yayness" + @krt.hostclass("boo").parent.should == "bar" + end + + it "should define the code when some is provided" do + @parser.parse("class foobar { $var = val }") + @krt.hostclass("foobar").code.should_not be_nil + end + + it "should define parameters when provided" do + @parser.parse("class foobar($biz,$baz) {}") + @krt.hostclass("foobar").arguments.should == {"biz" => nil, "baz" => nil} + end + end + + describe "when parsing resources" do + before :each do + @krt = Puppet::Resource::TypeCollection.new("development") + @parser = Puppet::Parser::Parser.new "development" + @parser.stubs(:known_resource_types).returns @krt + end + + it "should be able to parse class resources" do + @krt.add(Puppet::Resource::Type.new(:hostclass, "foobar", :arguments => {"biz" => nil})) + lambda { @parser.parse("class { foobar: biz => stuff }") }.should_not raise_error + end + + it "should correctly mark exported resources as exported" do + @parser.parse("@@file { '/file': }") + @krt.hostclass("").code[0].exported.should be_true + end + + it "should correctly mark virtual resources as virtual" do + @parser.parse("@file { '/file': }") + @krt.hostclass("").code[0].virtual.should be_true + end + end +end diff --git a/spec/unit/parser/parser_spec_spec.rb b/spec/unit/parser/parser_spec_spec.rb deleted file mode 100755 index ed4492a87..000000000 --- a/spec/unit/parser/parser_spec_spec.rb +++ /dev/null @@ -1,406 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Parser do - - ast = Puppet::Parser::AST - - before :each do - @known_resource_types = Puppet::Resource::TypeCollection.new("development") - @parser = Puppet::Parser::Parser.new "development" - @parser.stubs(:known_resource_types).returns @known_resource_types - @true_ast = Puppet::Parser::AST::Boolean.new :value => true - end - - it "should require an environment at initialization" do - lambda { Puppet::Parser::Parser.new }.should raise_error(ArgumentError) - end - - it "should set the environment" do - env = Puppet::Node::Environment.new - Puppet::Parser::Parser.new(env).environment.should == env - end - - it "should convert the environment into an environment instance if a string is provided" do - env = Puppet::Node::Environment.new("testing") - Puppet::Parser::Parser.new("testing").environment.should == env - end - - it "should be able to look up the environment-specific resource type collection" do - rtc = Puppet::Node::Environment.new("development").known_resource_types - parser = Puppet::Parser::Parser.new "development" - parser.known_resource_types.should equal(rtc) - end - - it "should delegate importing to the known resource type loader" do - parser = Puppet::Parser::Parser.new "development" - parser.known_resource_types.loader.expects(:import).with("newfile", "current_file") - parser.lexer.expects(:file).returns "current_file" - parser.import("newfile") - end - - describe "when parsing files" do - before do - FileTest.stubs(:exist?).returns true - File.stubs(:read).returns "" - @parser.stubs(:watch_file) - end - - it "should treat files ending in 'rb' as ruby files" do - @parser.expects(:parse_ruby_file) - @parser.file = "/my/file.rb" - @parser.parse - end - - describe "in ruby" do - it "should use the ruby interpreter to load the file" do - @parser.file = "/my/file.rb" - @parser.expects(:require).with "/my/file.rb" - - @parser.parse_ruby_file - end - end - end - - describe "when parsing append operator" do - - it "should not raise syntax errors" do - lambda { @parser.parse("$var += something") }.should_not raise_error - end - - it "shouldraise syntax error on incomplete syntax " do - lambda { @parser.parse("$var += ") }.should raise_error - end - - it "should call ast::VarDef with append=true" do - ast::VarDef.expects(:new).with { |h| h[:append] == true } - @parser.parse("$var += 2") - end - - it "should work with arrays too" do - ast::VarDef.expects(:new).with { |h| h[:append] == true } - @parser.parse("$var += ['test']") - end - - end - - describe "when parsing 'if'" do - it "not, it should create the correct ast objects" do - ast::Not.expects(:new).with { |h| h[:value].is_a?(ast::Boolean) } - @parser.parse("if ! true { $var = 1 }") - end - - it "boolean operation, it should create the correct ast objects" do - ast::BooleanOperator.expects(:new).with { - |h| h[:rval].is_a?(ast::Boolean) and h[:lval].is_a?(ast::Boolean) and h[:operator]=="or" - } - @parser.parse("if true or true { $var = 1 }") - - end - - it "comparison operation, it should create the correct ast objects" do - ast::ComparisonOperator.expects(:new).with { - |h| h[:lval].is_a?(ast::Name) and h[:rval].is_a?(ast::Name) and h[:operator]=="<" - } - @parser.parse("if 1 < 2 { $var = 1 }") - - end - - end - - describe "when parsing if complex expressions" do - it "should create a correct ast tree" do - aststub = stub_everything 'ast' - ast::ComparisonOperator.expects(:new).with { - |h| h[:rval].is_a?(ast::Name) and h[:lval].is_a?(ast::Name) and h[:operator]==">" - }.returns(aststub) - ast::ComparisonOperator.expects(:new).with { - |h| h[:rval].is_a?(ast::Name) and h[:lval].is_a?(ast::Name) and h[:operator]=="==" - }.returns(aststub) - ast::BooleanOperator.expects(:new).with { - |h| h[:rval]==aststub and h[:lval]==aststub and h[:operator]=="and" - } - @parser.parse("if (1 > 2) and (1 == 2) { $var = 1 }") - end - - it "should raise an error on incorrect expression" do - lambda { @parser.parse("if (1 > 2 > ) or (1 == 2) { $var = 1 }") }.should raise_error - end - - end - - describe "when parsing resource references" do - - it "should not raise syntax errors" do - lambda { @parser.parse('exec { test: param => File["a"] }') }.should_not raise_error - end - - it "should not raise syntax errors with multiple references" do - lambda { @parser.parse('exec { test: param => File["a","b"] }') }.should_not raise_error - end - - it "should create an ast::ResourceReference" do - ast::Resource.stubs(:new) - ast::ResourceReference.expects(:new).with { |arg| - arg[:line]==1 and arg[:type]=="File" and arg[:title].is_a?(ast::ASTArray) - } - @parser.parse('exec { test: command => File["a","b"] }') - end - end - - describe "when parsing resource overrides" do - - it "should not raise syntax errors" do - lambda { @parser.parse('Resource["title"] { param => value }') }.should_not raise_error - end - - it "should not raise syntax errors with multiple overrides" do - lambda { @parser.parse('Resource["title1","title2"] { param => value }') }.should_not raise_error - end - - it "should create an ast::ResourceOverride" do - ast::ResourceOverride.expects(:new).with { |arg| - arg[:line]==1 and arg[:object].is_a?(ast::ResourceReference) and arg[:parameters].is_a?(ast::ResourceParam) - } - @parser.parse('Resource["title1","title2"] { param => value }') - end - - end - - describe "when parsing if statements" do - - it "should not raise errors with empty if" do - lambda { @parser.parse("if true { }") }.should_not raise_error - end - - it "should not raise errors with empty else" do - lambda { @parser.parse("if false { notice('if') } else { }") }.should_not raise_error - end - - it "should not raise errors with empty if and else" do - lambda { @parser.parse("if false { } else { }") }.should_not raise_error - end - - it "should create a nop node for empty branch" do - ast::Nop.expects(:new) - @parser.parse("if true { }") - end - - it "should create a nop node for empty else branch" do - ast::Nop.expects(:new) - @parser.parse("if true { notice('test') } else { }") - end - - end - - describe "when parsing function calls" do - - it "should not raise errors with no arguments" do - lambda { @parser.parse("tag()") }.should_not raise_error - end - - it "should not raise errors with rvalue function with no args" do - lambda { @parser.parse("$a = template()") }.should_not raise_error - end - - it "should not raise errors with arguments" do - lambda { @parser.parse("notice(1)") }.should_not raise_error - end - - it "should not raise errors with multiple arguments" do - lambda { @parser.parse("notice(1,2)") }.should_not raise_error - end - - it "should not raise errors with multiple arguments and a trailing comma" do - lambda { @parser.parse("notice(1,2,)") }.should_not raise_error - end - - end - - describe "when parsing arrays with trailing comma" do - - it "should not raise errors with a trailing comma" do - lambda { @parser.parse("$a = [1,2,]") }.should_not raise_error - end - end - - describe "when providing AST context" do - before do - @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" - @parser.stubs(:lexer).returns @lexer - end - - it "should include the lexer's line" do - @parser.ast_context[:line].should == 50 - end - - it "should include the lexer's file" do - @parser.ast_context[:file].should == "/foo/bar" - end - - it "should include the docs if directed to do so" do - @parser.ast_context(true)[:doc].should == "whev" - end - - it "should not include the docs when told not to" do - @parser.ast_context(false)[:doc].should be_nil - end - - it "should not include the docs by default" do - @parser.ast_context()[:doc].should be_nil - end - end - - describe "when building ast nodes" do - before do - @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" - @parser.stubs(:lexer).returns @lexer - @class = stub 'class', :use_docs => false - end - - it "should return a new instance of the provided class created with the provided options" do - @class.expects(:new).with { |opts| opts[:foo] == "bar" } - @parser.ast(@class, :foo => "bar") - end - - it "should merge the ast context into the provided options" do - @class.expects(:new).with { |opts| opts[:file] == "/foo" } - @parser.expects(:ast_context).returns :file => "/foo" - @parser.ast(@class, :foo => "bar") - end - - it "should prefer provided options over AST context" do - @class.expects(:new).with { |opts| opts[:file] == "/bar" } - @parser.expects(:ast_context).returns :file => "/foo" - @parser.ast(@class, :file => "/bar") - end - - it "should include docs when the AST class uses them" do - @class.expects(:use_docs).returns true - @class.stubs(:new) - @parser.expects(:ast_context).with(true).returns({}) - @parser.ast(@class, :file => "/bar") - end - end - - describe "when creating a node" do - before :each do - @lexer = stub 'lexer' - @lexer.stubs(:getcomment) - @parser.stubs(:lexer).returns(@lexer) - @node = stub_everything 'node' - @parser.stubs(:ast_context).returns({}) - @parser.stubs(:node).returns(nil) - - @nodename = stub 'nodename', :is_a? => false, :value => "foo" - @nodename.stubs(:is_a?).with(Puppet::Parser::AST::HostName).returns(true) - end - - it "should return an array of nodes" do - @parser.newnode(@nodename).should be_instance_of(Array) - end - end - - describe "when retrieving a specific node" do - it "should delegate to the known_resource_types node" do - @known_resource_types.expects(:node).with("node") - - @parser.node("node") - end - end - - describe "when retrieving a specific class" do - it "should delegate to the loaded code" do - @known_resource_types.expects(:hostclass).with("class") - - @parser.hostclass("class") - end - end - - describe "when retrieving a specific definitions" do - it "should delegate to the loaded code" do - @known_resource_types.expects(:definition).with("define") - - @parser.definition("define") - end - end - - describe "when determining the configuration version" do - it "should determine it from the resource type collection" do - @parser.known_resource_types.expects(:version).returns "foo" - @parser.version.should == "foo" - end - end - - describe "when looking up definitions" do - it "should use the known resource types to check for them by name" do - @parser.known_resource_types.stubs(:find_or_load).with("namespace","name",:definition).returns(:this_value) - @parser.find_definition("namespace","name").should == :this_value - end - end - - describe "when looking up hostclasses" do - it "should use the known resource types to check for them by name" do - @parser.known_resource_types.stubs(:find_or_load).with("namespace","name",:hostclass).returns(:this_value) - @parser.find_hostclass("namespace","name").should == :this_value - end - end - - describe "when parsing classes" do - before :each do - @krt = Puppet::Resource::TypeCollection.new("development") - @parser = Puppet::Parser::Parser.new "development" - @parser.stubs(:known_resource_types).returns @krt - end - - it "should create new classes" do - @parser.parse("class foobar {}") - @krt.hostclass("foobar").should be_instance_of(Puppet::Resource::Type) - end - - it "should correctly set the parent class when one is provided" do - @parser.parse("class foobar inherits yayness {}") - @krt.hostclass("foobar").parent.should == "yayness" - end - - it "should correctly set the parent class for multiple classes at a time" do - @parser.parse("class foobar inherits yayness {}\nclass boo inherits bar {}") - @krt.hostclass("foobar").parent.should == "yayness" - @krt.hostclass("boo").parent.should == "bar" - end - - it "should define the code when some is provided" do - @parser.parse("class foobar { $var = val }") - @krt.hostclass("foobar").code.should_not be_nil - end - - it "should define parameters when provided" do - @parser.parse("class foobar($biz,$baz) {}") - @krt.hostclass("foobar").arguments.should == {"biz" => nil, "baz" => nil} - end - end - - describe "when parsing resources" do - before :each do - @krt = Puppet::Resource::TypeCollection.new("development") - @parser = Puppet::Parser::Parser.new "development" - @parser.stubs(:known_resource_types).returns @krt - end - - it "should be able to parse class resources" do - @krt.add(Puppet::Resource::Type.new(:hostclass, "foobar", :arguments => {"biz" => nil})) - lambda { @parser.parse("class { foobar: biz => stuff }") }.should_not raise_error - end - - it "should correctly mark exported resources as exported" do - @parser.parse("@@file { '/file': }") - @krt.hostclass("").code[0].exported.should be_true - end - - it "should correctly mark virtual resources as virtual" do - @parser.parse("@file { '/file': }") - @krt.hostclass("").code[0].virtual.should be_true - end - end -end diff --git a/spec/unit/parser/relationship_spec.rb b/spec/unit/parser/relationship_spec.rb new file mode 100644 index 000000000..bd4ff5632 --- /dev/null +++ b/spec/unit/parser/relationship_spec.rb @@ -0,0 +1,70 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/parser/relationship' + +describe Puppet::Parser::Relationship do + before do + @source = Puppet::Resource.new(:mytype, "source") + @target = Puppet::Resource.new(:mytype, "target") + @dep = Puppet::Parser::Relationship.new(@source, @target, :relationship) + end + + describe "when evaluating" do + before do + @catalog = Puppet::Resource::Catalog.new + @catalog.add_resource(@source) + @catalog.add_resource(@target) + end + + it "should fail if the source resource cannot be found" do + @catalog = Puppet::Resource::Catalog.new + @catalog.add_resource @target + lambda { @dep.evaluate(@catalog) }.should raise_error(ArgumentError) + end + + it "should fail if the target resource cannot be found" do + @catalog = Puppet::Resource::Catalog.new + @catalog.add_resource @source + lambda { @dep.evaluate(@catalog) }.should raise_error(ArgumentError) + end + + it "should add the target as a 'before' value if the type is 'relationship'" do + @dep.type = :relationship + @dep.evaluate(@catalog) + @source[:before].should be_include("Mytype[target]") + end + + it "should add the target as a 'notify' value if the type is 'subscription'" do + @dep.type = :subscription + @dep.evaluate(@catalog) + @source[:notify].should be_include("Mytype[target]") + end + + it "should supplement rather than clobber existing relationship values" do + @source[:before] = "File[/bar]" + @dep.evaluate(@catalog) + @source[:before].should be_include("Mytype[target]") + @source[:before].should be_include("File[/bar]") + end + + it "should use the collected retargets if the target is a Collector" do + orig_target = @target + @target = Puppet::Parser::Collector.new(stub("scope"), :file, "equery", "vquery", :virtual) + @target.collected[:foo] = @target + @dep.evaluate(@catalog) + + @source[:before].should be_include("Mytype[target]") + end + + it "should use the collected resources if the source is a Collector" do + orig_source = @source + @source = Puppet::Parser::Collector.new(stub("scope"), :file, "equery", "vquery", :virtual) + @source.collected[:foo] = @source + @dep.evaluate(@catalog) + + orig_source[:before].should be_include("Mytype[target]") + end + end +end diff --git a/spec/unit/parser/relationship_spec_spec.rb b/spec/unit/parser/relationship_spec_spec.rb deleted file mode 100644 index bd4ff5632..000000000 --- a/spec/unit/parser/relationship_spec_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/parser/relationship' - -describe Puppet::Parser::Relationship do - before do - @source = Puppet::Resource.new(:mytype, "source") - @target = Puppet::Resource.new(:mytype, "target") - @dep = Puppet::Parser::Relationship.new(@source, @target, :relationship) - end - - describe "when evaluating" do - before do - @catalog = Puppet::Resource::Catalog.new - @catalog.add_resource(@source) - @catalog.add_resource(@target) - end - - it "should fail if the source resource cannot be found" do - @catalog = Puppet::Resource::Catalog.new - @catalog.add_resource @target - lambda { @dep.evaluate(@catalog) }.should raise_error(ArgumentError) - end - - it "should fail if the target resource cannot be found" do - @catalog = Puppet::Resource::Catalog.new - @catalog.add_resource @source - lambda { @dep.evaluate(@catalog) }.should raise_error(ArgumentError) - end - - it "should add the target as a 'before' value if the type is 'relationship'" do - @dep.type = :relationship - @dep.evaluate(@catalog) - @source[:before].should be_include("Mytype[target]") - end - - it "should add the target as a 'notify' value if the type is 'subscription'" do - @dep.type = :subscription - @dep.evaluate(@catalog) - @source[:notify].should be_include("Mytype[target]") - end - - it "should supplement rather than clobber existing relationship values" do - @source[:before] = "File[/bar]" - @dep.evaluate(@catalog) - @source[:before].should be_include("Mytype[target]") - @source[:before].should be_include("File[/bar]") - end - - it "should use the collected retargets if the target is a Collector" do - orig_target = @target - @target = Puppet::Parser::Collector.new(stub("scope"), :file, "equery", "vquery", :virtual) - @target.collected[:foo] = @target - @dep.evaluate(@catalog) - - @source[:before].should be_include("Mytype[target]") - end - - it "should use the collected resources if the source is a Collector" do - orig_source = @source - @source = Puppet::Parser::Collector.new(stub("scope"), :file, "equery", "vquery", :virtual) - @source.collected[:foo] = @source - @dep.evaluate(@catalog) - - orig_source[:before].should be_include("Mytype[target]") - end - end -end diff --git a/spec/unit/parser/resource_spec.rb b/spec/unit/parser/resource_spec.rb new file mode 100755 index 000000000..9d407c0e7 --- /dev/null +++ b/spec/unit/parser/resource_spec.rb @@ -0,0 +1,564 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +# LAK: FIXME This is just new tests for resources; I have +# not moved all tests over yet. + +describe Puppet::Parser::Resource do + before do + @node = Puppet::Node.new("yaynode") + @known_resource_types = Puppet::Resource::TypeCollection.new("env") + @compiler = Puppet::Parser::Compiler.new(@node) + @compiler.environment.stubs(:known_resource_types).returns @known_resource_types + @source = newclass "" + @scope = @compiler.topscope + end + + def mkresource(args = {}) + args[:source] ||= @source + args[:scope] ||= @scope + + params = args[:parameters] || {:one => "yay", :three => "rah"} + if args[:parameters] == :none + args.delete(:parameters) + elsif not args[:parameters].is_a? Array + args[:parameters] = paramify(args[:source], params) + end + + Puppet::Parser::Resource.new("resource", "testing", args) + end + + def param(name, value, source) + Puppet::Parser::Resource::Param.new(:name => name, :value => value, :source => source) + end + + def paramify(source, hash) + hash.collect do |name, value| + Puppet::Parser::Resource::Param.new( + :name => name, :value => value, :source => source + ) + end + end + + def newclass(name) + @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) + end + + def newdefine(name) + @known_resource_types.add Puppet::Resource::Type.new(:definition, name) + end + + def newnode(name) + @known_resource_types.add Puppet::Resource::Type.new(:node, name) + end + + it "should use the file lookup module" do + Puppet::Parser::Resource.ancestors.should be_include(Puppet::FileCollection::Lookup) + end + + it "should get its environment from its scope" do + scope = stub 'scope', :source => stub("source") + scope.expects(:environment).returns "foo" + Puppet::Parser::Resource.new("file", "whatever", :scope => scope).environment.should == "foo" + end + + it "should get its namespaces from its scope" do + scope = stub 'scope', :source => stub("source") + scope.expects(:namespaces).returns %w{one two} + Puppet::Parser::Resource.new("file", "whatever", :scope => scope).namespaces.should == %w{one two} + end + + it "should use the resource type collection helper module" do + Puppet::Parser::Resource.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) + end + + it "should use the scope's environment as its environment" do + @scope.expects(:environment).returns "myenv" + Puppet::Parser::Resource.new("file", "whatever", :scope => @scope).environment.should == "myenv" + end + + it "should be isomorphic if it is builtin and models an isomorphic type" do + Puppet::Type.type(:file).expects(:isomorphic?).returns(true) + @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true + end + + it "should not be isomorphic if it is builtin and models a non-isomorphic type" do + Puppet::Type.type(:file).expects(:isomorphic?).returns(false) + @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_false + end + + it "should be isomorphic if it is not builtin" do + newdefine "whatever" + @resource = Puppet::Parser::Resource.new("whatever", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true + end + + it "should have a array-indexing method for retrieving parameter values" do + @resource = mkresource + @resource[:one].should == "yay" + end + + it "should use a Puppet::Resource for converting to a ral resource" do + trans = mock 'resource', :to_ral => "yay" + @resource = mkresource + @resource.expects(:to_resource).returns trans + @resource.to_ral.should == "yay" + end + + it "should be able to use the indexing operator to access parameters" do + resource = Puppet::Parser::Resource.new("resource", "testing", :source => "source", :scope => @scope) + resource["foo"] = "bar" + resource["foo"].should == "bar" + end + + it "should return the title when asked for a parameter named 'title'" do + Puppet::Parser::Resource.new("resource", "testing", :source => @source, :scope => @scope)[:title].should == "testing" + end + + describe "when initializing" do + before do + @arguments = {:scope => @scope} + end + + it "should fail unless #{name.to_s} is specified" do + lambda { Puppet::Parser::Resource.new('file', '/my/file') }.should raise_error(ArgumentError) + end + + it "should set the reference correctly" do + res = Puppet::Parser::Resource.new("resource", "testing", @arguments) + res.ref.should == "Resource[testing]" + end + + it "should be tagged with user tags" do + tags = [ "tag1", "tag2" ] + @arguments[:parameters] = [ param(:tag, tags , :source) ] + res = Puppet::Parser::Resource.new("resource", "testing", @arguments) + (res.tags & tags).should == tags + end + end + + describe "when evaluating" do + it "should evaluate the associated AST definition" do + definition = newdefine "mydefine" + res = Puppet::Parser::Resource.new("mydefine", "whatever", :scope => @scope, :source => @source) + definition.expects(:evaluate_code).with(res) + + res.evaluate + end + + it "should evaluate the associated AST class" do + @class = newclass "myclass" + res = Puppet::Parser::Resource.new("class", "myclass", :scope => @scope, :source => @source) + @class.expects(:evaluate_code).with(res) + res.evaluate + end + + it "should evaluate the associated AST node" do + nodedef = newnode("mynode") + res = Puppet::Parser::Resource.new("node", "mynode", :scope => @scope, :source => @source) + nodedef.expects(:evaluate_code).with(res) + res.evaluate + end + end + + describe "when finishing" do + before do + @class = newclass "myclass" + @nodedef = newnode("mynode") + + @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source) + end + + it "should do nothing if it has already been finished" do + @resource.finish + @resource.expects(:add_metaparams).never + @resource.finish + end + + it "should add all defaults available from the scope" do + @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => param(:owner, "default", @resource.source)) + @resource.finish + + @resource[:owner].should == "default" + end + + it "should not replace existing parameters with defaults" do + @resource.set_parameter :owner, "oldvalue" + @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => :replaced) + @resource.finish + + @resource[:owner].should == "oldvalue" + end + + it "should add a copy of each default, rather than the actual default parameter instance" do + newparam = param(:owner, "default", @resource.source) + other = newparam.dup + other.value = "other" + newparam.expects(:dup).returns(other) + @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => newparam) + @resource.finish + + @resource[:owner].should == "other" + end + + it "should be running in metaparam compatibility mode if running a version below 0.25" do + catalog = stub 'catalog', :client_version => "0.24.8" + @resource.stubs(:catalog).returns catalog + @resource.should be_metaparam_compatibility_mode + end + + it "should be running in metaparam compatibility mode if running no client version is available" do + catalog = stub 'catalog', :client_version => nil + @resource.stubs(:catalog).returns catalog + @resource.should be_metaparam_compatibility_mode + end + + it "should not be running in metaparam compatibility mode if running a version at or above 0.25" do + catalog = stub 'catalog', :client_version => "0.25.0" + @resource.stubs(:catalog).returns catalog + @resource.should_not be_metaparam_compatibility_mode + end + + it "should not copy relationship metaparams when not in metaparam compatibility mode" do + @scope.setvar("require", "bar") + + @resource.stubs(:metaparam_compatibility_mode?).returns false + @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } + + @resource["require"].should be_nil + end + + it "should copy relationship metaparams when in metaparam compatibility mode" do + @scope.setvar("require", "bar") + + @resource.stubs(:metaparam_compatibility_mode?).returns true + @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } + + @resource["require"].should == "bar" + end + + it "should stack relationship metaparams when in metaparam compatibility mode" do + @resource.set_parameter("require", "foo") + @scope.setvar("require", "bar") + + @resource.stubs(:metaparam_compatibility_mode?).returns true + @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } + + @resource["require"].should == ["foo", "bar"] + end + end + + describe "when being tagged" do + before do + @scope_resource = stub 'scope_resource', :tags => %w{srone srtwo} + @scope.stubs(:resource).returns @scope_resource + @resource = Puppet::Parser::Resource.new("file", "yay", :scope => @scope, :source => mock('source')) + end + + it "should get tagged with the resource type" do + @resource.tags.should be_include("file") + end + + it "should get tagged with the title" do + @resource.tags.should be_include("yay") + end + + it "should get tagged with each name in the title if the title is a qualified class name" do + resource = Puppet::Parser::Resource.new("file", "one::two", :scope => @scope, :source => mock('source')) + resource.tags.should be_include("one") + resource.tags.should be_include("two") + end + + it "should get tagged with each name in the type if the type is a qualified class name" do + resource = Puppet::Parser::Resource.new("one::two", "whatever", :scope => @scope, :source => mock('source')) + resource.tags.should be_include("one") + resource.tags.should be_include("two") + end + + it "should not get tagged with non-alphanumeric titles" do + resource = Puppet::Parser::Resource.new("file", "this is a test", :scope => @scope, :source => mock('source')) + resource.tags.should_not be_include("this is a test") + end + + it "should fail on tags containing '*' characters" do + lambda { @resource.tag("bad*tag") }.should raise_error(Puppet::ParseError) + end + + it "should fail on tags starting with '-' characters" do + lambda { @resource.tag("-badtag") }.should raise_error(Puppet::ParseError) + end + + it "should fail on tags containing ' ' characters" do + lambda { @resource.tag("bad tag") }.should raise_error(Puppet::ParseError) + end + + it "should allow alpha tags" do + lambda { @resource.tag("good_tag") }.should_not raise_error(Puppet::ParseError) + end + end + + describe "when merging overrides" do + before do + @source = "source1" + @resource = mkresource :source => @source + @override = mkresource :source => @source + end + + it "should fail when the override was not created by a parent class" do + @override.source = "source2" + @override.source.expects(:child_of?).with("source1").returns(false) + lambda { @resource.merge(@override) }.should raise_error(Puppet::ParseError) + end + + it "should succeed when the override was created in the current scope" do + @resource.source = "source3" + @override.source = @resource.source + @override.source.expects(:child_of?).with("source3").never + params = {:a => :b, :c => :d} + @override.expects(:parameters).returns(params) + @resource.expects(:override_parameter).with(:b) + @resource.expects(:override_parameter).with(:d) + @resource.merge(@override) + end + + it "should succeed when a parent class created the override" do + @resource.source = "source3" + @override.source = "source4" + @override.source.expects(:child_of?).with("source3").returns(true) + params = {:a => :b, :c => :d} + @override.expects(:parameters).returns(params) + @resource.expects(:override_parameter).with(:b) + @resource.expects(:override_parameter).with(:d) + @resource.merge(@override) + end + + it "should add new parameters when the parameter is not set" do + @source.stubs(:child_of?).returns true + @override.set_parameter(:testing, "value") + @resource.merge(@override) + + @resource[:testing].should == "value" + end + + it "should replace existing parameter values" do + @source.stubs(:child_of?).returns true + @resource.set_parameter(:testing, "old") + @override.set_parameter(:testing, "value") + + @resource.merge(@override) + + @resource[:testing].should == "value" + end + + it "should add values to the parameter when the override was created with the '+>' syntax" do + @source.stubs(:child_of?).returns true + param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "testing", :source => @resource.source) + param.add = true + + @override.set_parameter(param) + + @resource.set_parameter(:testing, "other") + + @resource.merge(@override) + + @resource[:testing].should == %w{other testing} + end + + it "should not merge parameter values when multiple resources are overriden with '+>' at once " do + @resource_2 = mkresource :source => @source + + @resource. set_parameter(:testing, "old_val_1") + @resource_2.set_parameter(:testing, "old_val_2") + + @source.stubs(:child_of?).returns true + param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "new_val", :source => @resource.source) + param.add = true + @override.set_parameter(param) + + @resource. merge(@override) + @resource_2.merge(@override) + + @resource [:testing].should == %w{old_val_1 new_val} + @resource_2[:testing].should == %w{old_val_2 new_val} + end + + it "should promote tag overrides to real tags" do + @source.stubs(:child_of?).returns true + param = Puppet::Parser::Resource::Param.new(:name => :tag, :value => "testing", :source => @resource.source) + + @override.set_parameter(param) + + @resource.merge(@override) + + @resource.tagged?("testing").should be_true + end + + end + + it "should be able to be converted to a normal resource" do + @source = stub 'scope', :name => "myscope" + @resource = mkresource :source => @source + @resource.should respond_to(:to_resource) + end + + it "should use its resource converter to convert to a transportable resource" do + @source = stub 'scope', :name => "myscope" + @resource = mkresource :source => @source + + newresource = Puppet::Resource.new(:file, "/my") + Puppet::Resource.expects(:new).returns(newresource) + + newresource.expects(:to_trans).returns "mytrans" + + @resource.to_trans.should == "mytrans" + end + + it "should return nil if converted to a transportable resource and it is virtual" do + @source = stub 'scope', :name => "myscope" + @resource = mkresource :source => @source + + @resource.expects(:virtual?).returns true + @resource.to_trans.should be_nil + end + + describe "when being converted to a resource" do + before do + @parser_resource = mkresource :scope => @scope, :parameters => {:foo => "bar", :fee => "fum"} + end + + it "should create an instance of Puppet::Resource" do + @parser_resource.to_resource.should be_instance_of(Puppet::Resource) + end + + it "should set the type correctly on the Puppet::Resource" do + @parser_resource.to_resource.type.should == @parser_resource.type + end + + it "should set the title correctly on the Puppet::Resource" do + @parser_resource.to_resource.title.should == @parser_resource.title + end + + it "should copy over all of the parameters" do + result = @parser_resource.to_resource.to_hash + + # The name will be in here, also. + result[:foo].should == "bar" + result[:fee].should == "fum" + end + + it "should copy over the tags" do + @parser_resource.tag "foo" + @parser_resource.tag "bar" + + @parser_resource.to_resource.tags.should == @parser_resource.tags + end + + it "should copy over the line" do + @parser_resource.line = 40 + @parser_resource.to_resource.line.should == 40 + end + + it "should copy over the file" do + @parser_resource.file = "/my/file" + @parser_resource.to_resource.file.should == "/my/file" + end + + it "should copy over the 'exported' value" do + @parser_resource.exported = true + @parser_resource.to_resource.exported.should be_true + end + + it "should copy over the 'virtual' value" do + @parser_resource.virtual = true + @parser_resource.to_resource.virtual.should be_true + end + + it "should convert any parser resource references to Puppet::Resource instances" do + ref = Puppet::Resource.new("file", "/my/file") + @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ref} + result = @parser_resource.to_resource + result[:fee].should == Puppet::Resource.new(:file, "/my/file") + end + + it "should convert any parser resource references to Puppet::Resource instances even if they are in an array" do + ref = Puppet::Resource.new("file", "/my/file") + @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", ref]} + result = @parser_resource.to_resource + result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file")] + end + + it "should convert any parser resource references to Puppet::Resource instances even if they are in an array of array, and even deeper" do + ref1 = Puppet::Resource.new("file", "/my/file1") + ref2 = Puppet::Resource.new("file", "/my/file2") + @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", [ref1,ref2]]} + result = @parser_resource.to_resource + result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file1"), Puppet::Resource.new(:file, "/my/file2")] + end + + it "should fail if the same param is declared twice" do + lambda do + @parser_resource = mkresource :source => @source, :parameters => [ + Puppet::Parser::Resource::Param.new( + :name => :foo, :value => "bar", :source => @source + ), + Puppet::Parser::Resource::Param.new( + :name => :foo, :value => "baz", :source => @source + ) + ] + end.should raise_error(Puppet::ParseError) + end + end + + describe "when validating" do + it "should check each parameter" do + resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => stub("source") + resource[:one] = :two + resource[:three] = :four + resource.expects(:validate_parameter).with(:one) + resource.expects(:validate_parameter).with(:three) + resource.send(:validate) + end + + it "should raise a parse error when there's a failure" do + resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => stub("source") + resource[:one] = :two + resource.expects(:validate_parameter).with(:one).raises ArgumentError + lambda { resource.send(:validate) }.should raise_error(Puppet::ParseError) + end + end + + describe "when setting parameters" do + before do + @source = newclass "foobar" + @resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => @source + end + + it "should accept Param instances and add them to the parameter list" do + param = Puppet::Parser::Resource::Param.new :name => "foo", :value => "bar", :source => @source + @resource.set_parameter(param) + @resource["foo"].should == "bar" + end + + it "should fail when provided a parameter name but no value" do + lambda { @resource.set_parameter("myparam") }.should raise_error(ArgumentError) + end + + it "should allow parameters to be set to 'false'" do + @resource.set_parameter("myparam", false) + @resource["myparam"].should be_false + end + + it "should use its source when provided a parameter name and value" do + @resource.set_parameter("myparam", "myvalue") + @resource["myparam"].should == "myvalue" + end + end + + # part of #629 -- the undef keyword. Make sure 'undef' params get skipped. + it "should not include 'undef' parameters when converting itself to a hash" do + resource = Puppet::Parser::Resource.new "file", "/tmp/testing", :source => mock("source"), :scope => mock("scope") + resource[:owner] = :undef + resource[:mode] = "755" + resource.to_hash[:owner].should be_nil + end +end diff --git a/spec/unit/parser/resource_spec_spec.rb b/spec/unit/parser/resource_spec_spec.rb deleted file mode 100755 index 9d407c0e7..000000000 --- a/spec/unit/parser/resource_spec_spec.rb +++ /dev/null @@ -1,564 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -# LAK: FIXME This is just new tests for resources; I have -# not moved all tests over yet. - -describe Puppet::Parser::Resource do - before do - @node = Puppet::Node.new("yaynode") - @known_resource_types = Puppet::Resource::TypeCollection.new("env") - @compiler = Puppet::Parser::Compiler.new(@node) - @compiler.environment.stubs(:known_resource_types).returns @known_resource_types - @source = newclass "" - @scope = @compiler.topscope - end - - def mkresource(args = {}) - args[:source] ||= @source - args[:scope] ||= @scope - - params = args[:parameters] || {:one => "yay", :three => "rah"} - if args[:parameters] == :none - args.delete(:parameters) - elsif not args[:parameters].is_a? Array - args[:parameters] = paramify(args[:source], params) - end - - Puppet::Parser::Resource.new("resource", "testing", args) - end - - def param(name, value, source) - Puppet::Parser::Resource::Param.new(:name => name, :value => value, :source => source) - end - - def paramify(source, hash) - hash.collect do |name, value| - Puppet::Parser::Resource::Param.new( - :name => name, :value => value, :source => source - ) - end - end - - def newclass(name) - @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) - end - - def newdefine(name) - @known_resource_types.add Puppet::Resource::Type.new(:definition, name) - end - - def newnode(name) - @known_resource_types.add Puppet::Resource::Type.new(:node, name) - end - - it "should use the file lookup module" do - Puppet::Parser::Resource.ancestors.should be_include(Puppet::FileCollection::Lookup) - end - - it "should get its environment from its scope" do - scope = stub 'scope', :source => stub("source") - scope.expects(:environment).returns "foo" - Puppet::Parser::Resource.new("file", "whatever", :scope => scope).environment.should == "foo" - end - - it "should get its namespaces from its scope" do - scope = stub 'scope', :source => stub("source") - scope.expects(:namespaces).returns %w{one two} - Puppet::Parser::Resource.new("file", "whatever", :scope => scope).namespaces.should == %w{one two} - end - - it "should use the resource type collection helper module" do - Puppet::Parser::Resource.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) - end - - it "should use the scope's environment as its environment" do - @scope.expects(:environment).returns "myenv" - Puppet::Parser::Resource.new("file", "whatever", :scope => @scope).environment.should == "myenv" - end - - it "should be isomorphic if it is builtin and models an isomorphic type" do - Puppet::Type.type(:file).expects(:isomorphic?).returns(true) - @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true - end - - it "should not be isomorphic if it is builtin and models a non-isomorphic type" do - Puppet::Type.type(:file).expects(:isomorphic?).returns(false) - @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_false - end - - it "should be isomorphic if it is not builtin" do - newdefine "whatever" - @resource = Puppet::Parser::Resource.new("whatever", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true - end - - it "should have a array-indexing method for retrieving parameter values" do - @resource = mkresource - @resource[:one].should == "yay" - end - - it "should use a Puppet::Resource for converting to a ral resource" do - trans = mock 'resource', :to_ral => "yay" - @resource = mkresource - @resource.expects(:to_resource).returns trans - @resource.to_ral.should == "yay" - end - - it "should be able to use the indexing operator to access parameters" do - resource = Puppet::Parser::Resource.new("resource", "testing", :source => "source", :scope => @scope) - resource["foo"] = "bar" - resource["foo"].should == "bar" - end - - it "should return the title when asked for a parameter named 'title'" do - Puppet::Parser::Resource.new("resource", "testing", :source => @source, :scope => @scope)[:title].should == "testing" - end - - describe "when initializing" do - before do - @arguments = {:scope => @scope} - end - - it "should fail unless #{name.to_s} is specified" do - lambda { Puppet::Parser::Resource.new('file', '/my/file') }.should raise_error(ArgumentError) - end - - it "should set the reference correctly" do - res = Puppet::Parser::Resource.new("resource", "testing", @arguments) - res.ref.should == "Resource[testing]" - end - - it "should be tagged with user tags" do - tags = [ "tag1", "tag2" ] - @arguments[:parameters] = [ param(:tag, tags , :source) ] - res = Puppet::Parser::Resource.new("resource", "testing", @arguments) - (res.tags & tags).should == tags - end - end - - describe "when evaluating" do - it "should evaluate the associated AST definition" do - definition = newdefine "mydefine" - res = Puppet::Parser::Resource.new("mydefine", "whatever", :scope => @scope, :source => @source) - definition.expects(:evaluate_code).with(res) - - res.evaluate - end - - it "should evaluate the associated AST class" do - @class = newclass "myclass" - res = Puppet::Parser::Resource.new("class", "myclass", :scope => @scope, :source => @source) - @class.expects(:evaluate_code).with(res) - res.evaluate - end - - it "should evaluate the associated AST node" do - nodedef = newnode("mynode") - res = Puppet::Parser::Resource.new("node", "mynode", :scope => @scope, :source => @source) - nodedef.expects(:evaluate_code).with(res) - res.evaluate - end - end - - describe "when finishing" do - before do - @class = newclass "myclass" - @nodedef = newnode("mynode") - - @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source) - end - - it "should do nothing if it has already been finished" do - @resource.finish - @resource.expects(:add_metaparams).never - @resource.finish - end - - it "should add all defaults available from the scope" do - @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => param(:owner, "default", @resource.source)) - @resource.finish - - @resource[:owner].should == "default" - end - - it "should not replace existing parameters with defaults" do - @resource.set_parameter :owner, "oldvalue" - @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => :replaced) - @resource.finish - - @resource[:owner].should == "oldvalue" - end - - it "should add a copy of each default, rather than the actual default parameter instance" do - newparam = param(:owner, "default", @resource.source) - other = newparam.dup - other.value = "other" - newparam.expects(:dup).returns(other) - @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => newparam) - @resource.finish - - @resource[:owner].should == "other" - end - - it "should be running in metaparam compatibility mode if running a version below 0.25" do - catalog = stub 'catalog', :client_version => "0.24.8" - @resource.stubs(:catalog).returns catalog - @resource.should be_metaparam_compatibility_mode - end - - it "should be running in metaparam compatibility mode if running no client version is available" do - catalog = stub 'catalog', :client_version => nil - @resource.stubs(:catalog).returns catalog - @resource.should be_metaparam_compatibility_mode - end - - it "should not be running in metaparam compatibility mode if running a version at or above 0.25" do - catalog = stub 'catalog', :client_version => "0.25.0" - @resource.stubs(:catalog).returns catalog - @resource.should_not be_metaparam_compatibility_mode - end - - it "should not copy relationship metaparams when not in metaparam compatibility mode" do - @scope.setvar("require", "bar") - - @resource.stubs(:metaparam_compatibility_mode?).returns false - @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } - - @resource["require"].should be_nil - end - - it "should copy relationship metaparams when in metaparam compatibility mode" do - @scope.setvar("require", "bar") - - @resource.stubs(:metaparam_compatibility_mode?).returns true - @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } - - @resource["require"].should == "bar" - end - - it "should stack relationship metaparams when in metaparam compatibility mode" do - @resource.set_parameter("require", "foo") - @scope.setvar("require", "bar") - - @resource.stubs(:metaparam_compatibility_mode?).returns true - @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } - - @resource["require"].should == ["foo", "bar"] - end - end - - describe "when being tagged" do - before do - @scope_resource = stub 'scope_resource', :tags => %w{srone srtwo} - @scope.stubs(:resource).returns @scope_resource - @resource = Puppet::Parser::Resource.new("file", "yay", :scope => @scope, :source => mock('source')) - end - - it "should get tagged with the resource type" do - @resource.tags.should be_include("file") - end - - it "should get tagged with the title" do - @resource.tags.should be_include("yay") - end - - it "should get tagged with each name in the title if the title is a qualified class name" do - resource = Puppet::Parser::Resource.new("file", "one::two", :scope => @scope, :source => mock('source')) - resource.tags.should be_include("one") - resource.tags.should be_include("two") - end - - it "should get tagged with each name in the type if the type is a qualified class name" do - resource = Puppet::Parser::Resource.new("one::two", "whatever", :scope => @scope, :source => mock('source')) - resource.tags.should be_include("one") - resource.tags.should be_include("two") - end - - it "should not get tagged with non-alphanumeric titles" do - resource = Puppet::Parser::Resource.new("file", "this is a test", :scope => @scope, :source => mock('source')) - resource.tags.should_not be_include("this is a test") - end - - it "should fail on tags containing '*' characters" do - lambda { @resource.tag("bad*tag") }.should raise_error(Puppet::ParseError) - end - - it "should fail on tags starting with '-' characters" do - lambda { @resource.tag("-badtag") }.should raise_error(Puppet::ParseError) - end - - it "should fail on tags containing ' ' characters" do - lambda { @resource.tag("bad tag") }.should raise_error(Puppet::ParseError) - end - - it "should allow alpha tags" do - lambda { @resource.tag("good_tag") }.should_not raise_error(Puppet::ParseError) - end - end - - describe "when merging overrides" do - before do - @source = "source1" - @resource = mkresource :source => @source - @override = mkresource :source => @source - end - - it "should fail when the override was not created by a parent class" do - @override.source = "source2" - @override.source.expects(:child_of?).with("source1").returns(false) - lambda { @resource.merge(@override) }.should raise_error(Puppet::ParseError) - end - - it "should succeed when the override was created in the current scope" do - @resource.source = "source3" - @override.source = @resource.source - @override.source.expects(:child_of?).with("source3").never - params = {:a => :b, :c => :d} - @override.expects(:parameters).returns(params) - @resource.expects(:override_parameter).with(:b) - @resource.expects(:override_parameter).with(:d) - @resource.merge(@override) - end - - it "should succeed when a parent class created the override" do - @resource.source = "source3" - @override.source = "source4" - @override.source.expects(:child_of?).with("source3").returns(true) - params = {:a => :b, :c => :d} - @override.expects(:parameters).returns(params) - @resource.expects(:override_parameter).with(:b) - @resource.expects(:override_parameter).with(:d) - @resource.merge(@override) - end - - it "should add new parameters when the parameter is not set" do - @source.stubs(:child_of?).returns true - @override.set_parameter(:testing, "value") - @resource.merge(@override) - - @resource[:testing].should == "value" - end - - it "should replace existing parameter values" do - @source.stubs(:child_of?).returns true - @resource.set_parameter(:testing, "old") - @override.set_parameter(:testing, "value") - - @resource.merge(@override) - - @resource[:testing].should == "value" - end - - it "should add values to the parameter when the override was created with the '+>' syntax" do - @source.stubs(:child_of?).returns true - param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "testing", :source => @resource.source) - param.add = true - - @override.set_parameter(param) - - @resource.set_parameter(:testing, "other") - - @resource.merge(@override) - - @resource[:testing].should == %w{other testing} - end - - it "should not merge parameter values when multiple resources are overriden with '+>' at once " do - @resource_2 = mkresource :source => @source - - @resource. set_parameter(:testing, "old_val_1") - @resource_2.set_parameter(:testing, "old_val_2") - - @source.stubs(:child_of?).returns true - param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "new_val", :source => @resource.source) - param.add = true - @override.set_parameter(param) - - @resource. merge(@override) - @resource_2.merge(@override) - - @resource [:testing].should == %w{old_val_1 new_val} - @resource_2[:testing].should == %w{old_val_2 new_val} - end - - it "should promote tag overrides to real tags" do - @source.stubs(:child_of?).returns true - param = Puppet::Parser::Resource::Param.new(:name => :tag, :value => "testing", :source => @resource.source) - - @override.set_parameter(param) - - @resource.merge(@override) - - @resource.tagged?("testing").should be_true - end - - end - - it "should be able to be converted to a normal resource" do - @source = stub 'scope', :name => "myscope" - @resource = mkresource :source => @source - @resource.should respond_to(:to_resource) - end - - it "should use its resource converter to convert to a transportable resource" do - @source = stub 'scope', :name => "myscope" - @resource = mkresource :source => @source - - newresource = Puppet::Resource.new(:file, "/my") - Puppet::Resource.expects(:new).returns(newresource) - - newresource.expects(:to_trans).returns "mytrans" - - @resource.to_trans.should == "mytrans" - end - - it "should return nil if converted to a transportable resource and it is virtual" do - @source = stub 'scope', :name => "myscope" - @resource = mkresource :source => @source - - @resource.expects(:virtual?).returns true - @resource.to_trans.should be_nil - end - - describe "when being converted to a resource" do - before do - @parser_resource = mkresource :scope => @scope, :parameters => {:foo => "bar", :fee => "fum"} - end - - it "should create an instance of Puppet::Resource" do - @parser_resource.to_resource.should be_instance_of(Puppet::Resource) - end - - it "should set the type correctly on the Puppet::Resource" do - @parser_resource.to_resource.type.should == @parser_resource.type - end - - it "should set the title correctly on the Puppet::Resource" do - @parser_resource.to_resource.title.should == @parser_resource.title - end - - it "should copy over all of the parameters" do - result = @parser_resource.to_resource.to_hash - - # The name will be in here, also. - result[:foo].should == "bar" - result[:fee].should == "fum" - end - - it "should copy over the tags" do - @parser_resource.tag "foo" - @parser_resource.tag "bar" - - @parser_resource.to_resource.tags.should == @parser_resource.tags - end - - it "should copy over the line" do - @parser_resource.line = 40 - @parser_resource.to_resource.line.should == 40 - end - - it "should copy over the file" do - @parser_resource.file = "/my/file" - @parser_resource.to_resource.file.should == "/my/file" - end - - it "should copy over the 'exported' value" do - @parser_resource.exported = true - @parser_resource.to_resource.exported.should be_true - end - - it "should copy over the 'virtual' value" do - @parser_resource.virtual = true - @parser_resource.to_resource.virtual.should be_true - end - - it "should convert any parser resource references to Puppet::Resource instances" do - ref = Puppet::Resource.new("file", "/my/file") - @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ref} - result = @parser_resource.to_resource - result[:fee].should == Puppet::Resource.new(:file, "/my/file") - end - - it "should convert any parser resource references to Puppet::Resource instances even if they are in an array" do - ref = Puppet::Resource.new("file", "/my/file") - @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", ref]} - result = @parser_resource.to_resource - result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file")] - end - - it "should convert any parser resource references to Puppet::Resource instances even if they are in an array of array, and even deeper" do - ref1 = Puppet::Resource.new("file", "/my/file1") - ref2 = Puppet::Resource.new("file", "/my/file2") - @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", [ref1,ref2]]} - result = @parser_resource.to_resource - result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file1"), Puppet::Resource.new(:file, "/my/file2")] - end - - it "should fail if the same param is declared twice" do - lambda do - @parser_resource = mkresource :source => @source, :parameters => [ - Puppet::Parser::Resource::Param.new( - :name => :foo, :value => "bar", :source => @source - ), - Puppet::Parser::Resource::Param.new( - :name => :foo, :value => "baz", :source => @source - ) - ] - end.should raise_error(Puppet::ParseError) - end - end - - describe "when validating" do - it "should check each parameter" do - resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => stub("source") - resource[:one] = :two - resource[:three] = :four - resource.expects(:validate_parameter).with(:one) - resource.expects(:validate_parameter).with(:three) - resource.send(:validate) - end - - it "should raise a parse error when there's a failure" do - resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => stub("source") - resource[:one] = :two - resource.expects(:validate_parameter).with(:one).raises ArgumentError - lambda { resource.send(:validate) }.should raise_error(Puppet::ParseError) - end - end - - describe "when setting parameters" do - before do - @source = newclass "foobar" - @resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => @source - end - - it "should accept Param instances and add them to the parameter list" do - param = Puppet::Parser::Resource::Param.new :name => "foo", :value => "bar", :source => @source - @resource.set_parameter(param) - @resource["foo"].should == "bar" - end - - it "should fail when provided a parameter name but no value" do - lambda { @resource.set_parameter("myparam") }.should raise_error(ArgumentError) - end - - it "should allow parameters to be set to 'false'" do - @resource.set_parameter("myparam", false) - @resource["myparam"].should be_false - end - - it "should use its source when provided a parameter name and value" do - @resource.set_parameter("myparam", "myvalue") - @resource["myparam"].should == "myvalue" - end - end - - # part of #629 -- the undef keyword. Make sure 'undef' params get skipped. - it "should not include 'undef' parameters when converting itself to a hash" do - resource = Puppet::Parser::Resource.new "file", "/tmp/testing", :source => mock("source"), :scope => mock("scope") - resource[:owner] = :undef - resource[:mode] = "755" - resource.to_hash[:owner].should be_nil - end -end diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb new file mode 100755 index 000000000..29dca38f4 --- /dev/null +++ b/spec/unit/parser/scope_spec.rb @@ -0,0 +1,624 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Parser::Scope do + before :each do + @topscope = Puppet::Parser::Scope.new() + # This is necessary so we don't try to use the compiler to discover our parent. + @topscope.parent = nil + @scope = Puppet::Parser::Scope.new() + @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) + @scope.parent = @topscope + end + + it "should be able to store references to class scopes" do + lambda { @scope.class_set "myname", "myscope" }.should_not raise_error + end + + it "should be able to retrieve class scopes by name" do + @scope.class_set "myname", "myscope" + @scope.class_scope("myname").should == "myscope" + end + + it "should be able to retrieve class scopes by object" do + klass = mock 'ast_class' + klass.expects(:name).returns("myname") + @scope.class_set "myname", "myscope" + @scope.class_scope(klass).should == "myscope" + end + + it "should be able to retrieve its parent module name from the source of its parent type" do + @topscope.source = Puppet::Resource::Type.new(:hostclass, :foo) + @topscope.source.module_name = "foo" + + @scope.parent_module_name.should == "foo" + end + + it "should return a nil parent module name if it has no parent" do + @topscope.parent_module_name.should be_nil + end + + it "should return a nil parent module name if its parent has no source" do + @scope.parent_module_name.should be_nil + end + + it "should get its environment from its compiler" do + env = stub 'environment' + compiler = stub 'compiler', :environment => env + scope = Puppet::Parser::Scope.new :compiler => compiler + scope.environment.should equal(env) + end + + it "should use the resource type collection helper to find its known resource types" do + Puppet::Parser::Scope.ancestors.should include(Puppet::Resource::TypeCollectionHelper) + end + + describe "when initializing" do + it "should extend itself with its environment's Functions module as well as the default" do + env = Puppet::Node::Environment.new("myenv") + compiler = stub 'compiler', :environment => env + mod = Module.new + root_mod = Module.new + Puppet::Parser::Functions.expects(:environment_module).with(Puppet::Node::Environment.root).returns root_mod + Puppet::Parser::Functions.expects(:environment_module).with(env).returns mod + + Puppet::Parser::Scope.new(:compiler => compiler).singleton_class.ancestors.should be_include(mod) + end + + it "should extend itself with the default Functions module if it has no environment" do + mod = Module.new + Puppet::Parser::Functions.expects(:environment_module).with(Puppet::Node::Environment.root).returns(mod) + + Puppet::Parser::Functions.expects(:environment_module).with(nil).returns mod + + Puppet::Parser::Scope.new().singleton_class.ancestors.should be_include(mod) + end + end + + describe "when looking up a variable" do + it "should default to an empty string" do + @scope.lookupvar("var").should == "" + end + + it "should return an string when asked for a string" do + @scope.lookupvar("var", true).should == "" + end + + it "should return ':undefined' for unset variables when asked not to return a string" do + @scope.lookupvar("var", false).should == :undefined + end + + it "should be able to look up values" do + @scope.setvar("var", "yep") + @scope.lookupvar("var").should == "yep" + end + + it "should be able to look up hashes" do + @scope.setvar("var", {"a" => "b"}) + @scope.lookupvar("var").should == {"a" => "b"} + end + + it "should be able to look up variables in parent scopes" do + @topscope.setvar("var", "parentval") + @scope.lookupvar("var").should == "parentval" + end + + it "should prefer its own values to parent values" do + @topscope.setvar("var", "parentval") + @scope.setvar("var", "childval") + @scope.lookupvar("var").should == "childval" + end + + describe "and the variable is qualified" do + before do + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foonode")) + @scope.compiler = @compiler + @known_resource_types = @scope.known_resource_types + end + + def newclass(name) + @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) + end + + def create_class_scope(name) + klass = newclass(name) + Puppet::Parser::Resource.new("class", name, :scope => @scope, :source => mock('source')).evaluate + + return @scope.class_scope(klass) + end + + it "should be able to look up explicitly fully qualified variables from main" do + other_scope = create_class_scope("") + + other_scope.setvar("othervar", "otherval") + + @scope.lookupvar("::othervar").should == "otherval" + end + + it "should be able to look up explicitly fully qualified variables from other scopes" do + other_scope = create_class_scope("other") + + other_scope.setvar("var", "otherval") + + @scope.lookupvar("::other::var").should == "otherval" + end + + it "should be able to look up deeply qualified variables" do + other_scope = create_class_scope("other::deep::klass") + + other_scope.setvar("var", "otherval") + + @scope.lookupvar("other::deep::klass::var").should == "otherval" + end + + it "should return an empty string for qualified variables that cannot be found in other classes" do + other_scope = create_class_scope("other::deep::klass") + + @scope.lookupvar("other::deep::klass::var").should == "" + end + + it "should warn and return an empty string for qualified variables whose classes have not been evaluated" do + klass = newclass("other::deep::klass") + @scope.expects(:warning) + @scope.lookupvar("other::deep::klass::var").should == "" + end + + it "should warn and return an empty string for qualified variables whose classes do not exist" do + @scope.expects(:warning) + @scope.lookupvar("other::deep::klass::var").should == "" + end + + it "should return ':undefined' when asked for a non-string qualified variable from a class that does not exist" do + @scope.stubs(:warning) + @scope.lookupvar("other::deep::klass::var", false).should == :undefined + end + + it "should return ':undefined' when asked for a non-string qualified variable from a class that has not been evaluated" do + @scope.stubs(:warning) + klass = newclass("other::deep::klass") + @scope.lookupvar("other::deep::klass::var", false).should == :undefined + end + end + end + + describe "when setvar is called with append=true" do + it "should raise error if the variable is already defined in this scope" do + @scope.setvar("var","1", :append => false) + lambda { @scope.setvar("var","1", :append => true) }.should raise_error(Puppet::ParseError) + end + + it "should lookup current variable value" do + @scope.expects(:lookupvar).with("var").returns("2") + @scope.setvar("var","1", :append => true) + end + + it "should store the concatenated string '42'" do + @topscope.setvar("var","4", :append => false) + @scope.setvar("var","2", :append => true) + @scope.lookupvar("var").should == "42" + end + + it "should store the concatenated array [4,2]" do + @topscope.setvar("var",[4], :append => false) + @scope.setvar("var",[2], :append => true) + @scope.lookupvar("var").should == [4,2] + end + + it "should store the merged hash {a => b, c => d}" do + @topscope.setvar("var",{"a" => "b"}, :append => false) + @scope.setvar("var",{"c" => "d"}, :append => true) + @scope.lookupvar("var").should == {"a" => "b", "c" => "d"} + end + + it "should raise an error when appending a hash with something other than another hash" do + @topscope.setvar("var",{"a" => "b"}, :append => false) + lambda { @scope.setvar("var","not a hash", :append => true) }.should raise_error + end + end + + describe "when calling number?" do + it "should return nil if called with anything not a number" do + Puppet::Parser::Scope.number?([2]).should be_nil + end + + it "should return a Fixnum for a Fixnum" do + Puppet::Parser::Scope.number?(2).should be_an_instance_of(Fixnum) + end + + it "should return a Float for a Float" do + Puppet::Parser::Scope.number?(2.34).should be_an_instance_of(Float) + end + + it "should return 234 for '234'" do + Puppet::Parser::Scope.number?("234").should == 234 + end + + it "should return nil for 'not a number'" do + Puppet::Parser::Scope.number?("not a number").should be_nil + end + + it "should return 23.4 for '23.4'" do + Puppet::Parser::Scope.number?("23.4").should == 23.4 + end + + it "should return 23.4e13 for '23.4e13'" do + Puppet::Parser::Scope.number?("23.4e13").should == 23.4e13 + end + + it "should understand negative numbers" do + Puppet::Parser::Scope.number?("-234").should == -234 + end + + it "should know how to convert exponential float numbers ala '23e13'" do + Puppet::Parser::Scope.number?("23e13").should == 23e13 + end + + it "should understand hexadecimal numbers" do + Puppet::Parser::Scope.number?("0x234").should == 0x234 + end + + it "should understand octal numbers" do + Puppet::Parser::Scope.number?("0755").should == 0755 + end + + it "should return nil on malformed integers" do + Puppet::Parser::Scope.number?("0.24.5").should be_nil + end + + it "should convert strings with leading 0 to integer if they are not octal" do + Puppet::Parser::Scope.number?("0788").should == 788 + end + + it "should convert strings of negative integers" do + Puppet::Parser::Scope.number?("-0788").should == -788 + end + + it "should return nil on malformed hexadecimal numbers" do + Puppet::Parser::Scope.number?("0x89g").should be_nil + end + end + + describe "when using ephemeral variables" do + it "should store the variable value" do + @scope.setvar("1", :value, :ephemeral => true) + + @scope.lookupvar("1").should == :value + end + + it "should remove the variable value when unset_ephemeral_var is called" do + @scope.setvar("1", :value, :ephemeral => true) + @scope.stubs(:parent).returns(nil) + + @scope.unset_ephemeral_var + + @scope.lookupvar("1", false).should == :undefined + end + + it "should not remove classic variables when unset_ephemeral_var is called" do + @scope.setvar("myvar", :value1) + @scope.setvar("1", :value2, :ephemeral => true) + @scope.stubs(:parent).returns(nil) + + @scope.unset_ephemeral_var + + @scope.lookupvar("myvar", false).should == :value1 + end + + it "should raise an error when setting it again" do + @scope.setvar("1", :value2, :ephemeral => true) + lambda { @scope.setvar("1", :value3, :ephemeral => true) }.should raise_error + end + + it "should declare ephemeral number only variable names" do + @scope.ephemeral?("0").should be_true + end + + it "should not declare ephemeral other variable names" do + @scope.ephemeral?("abc0").should be_nil + end + + describe "with more than one level" do + it "should prefer latest ephemeral scopes" do + @scope.setvar("0", :earliest, :ephemeral => true) + @scope.new_ephemeral + @scope.setvar("0", :latest, :ephemeral => true) + @scope.lookupvar("0", false).should == :latest + end + + it "should be able to report the current level" do + @scope.ephemeral_level.should == 1 + @scope.new_ephemeral + @scope.ephemeral_level.should == 2 + end + + it "should check presence of an ephemeral variable accross multiple levels" do + @scope.new_ephemeral + @scope.setvar("1", :value1, :ephemeral => true) + @scope.new_ephemeral + @scope.setvar("0", :value2, :ephemeral => true) + @scope.new_ephemeral + @scope.ephemeral_include?("1").should be_true + end + + it "should return false when an ephemeral variable doesn't exist in any ephemeral scope" do + @scope.new_ephemeral + @scope.setvar("1", :value1, :ephemeral => true) + @scope.new_ephemeral + @scope.setvar("0", :value2, :ephemeral => true) + @scope.new_ephemeral + @scope.ephemeral_include?("2").should be_false + end + + it "should get ephemeral values from earlier scope when not in later" do + @scope.setvar("1", :value1, :ephemeral => true) + @scope.new_ephemeral + @scope.setvar("0", :value2, :ephemeral => true) + @scope.lookupvar("1", false).should == :value1 + end + + describe "when calling unset_ephemeral_var without a level" do + it "should remove all the variables values" do + @scope.setvar("1", :value1, :ephemeral => true) + @scope.new_ephemeral + @scope.setvar("1", :value2, :ephemeral => true) + + @scope.unset_ephemeral_var + + @scope.lookupvar("1", false).should == :undefined + end + end + + describe "when calling unset_ephemeral_var with a level" do + it "should remove ephemeral scopes up to this level" do + @scope.setvar("1", :value1, :ephemeral => true) + @scope.new_ephemeral + @scope.setvar("1", :value2, :ephemeral => true) + @scope.new_ephemeral + @scope.setvar("1", :value3, :ephemeral => true) + + @scope.unset_ephemeral_var(2) + + @scope.lookupvar("1", false).should == :value2 + end + end + end + end + + describe "when interpolating string" do + (0..9).each do |n| + it "should allow $#{n} to match" do + @scope.setvar(n.to_s, "value", :ephemeral => true) + + @scope.strinterp("$#{n}").should == "value" + end + end + + (0..9).each do |n| + it "should not allow $#{n} to match if not ephemeral" do + @scope.setvar(n.to_s, "value", :ephemeral => false) + + @scope.strinterp("$#{n}").should_not == "value" + end + end + + it "should not allow $10 to match" do + @scope.setvar("10", "value", :ephemeral => true) + + @scope.strinterp('==$10==').should_not == "==value==" + end + + it "should not allow ${10} to match" do + @scope.setvar("10", "value", :ephemeral => true) + + @scope.strinterp('==${10}==').should == "==value==" + end + + describe "with qualified variables" do + before do + @scopes = {} + klass = @scope.known_resource_types.add(Puppet::Resource::Type.new(:hostclass, "")) + Puppet::Parser::Resource.new("class", :main, :scope => @scope, :source => mock('source')).evaluate + @scopes[""] = @scope.class_scope(klass) + @scopes[""].setvar("test", "value") + + %w{one one::two one::two::three}.each do |name| + klass = @scope.known_resource_types.add(Puppet::Resource::Type.new(:hostclass, name)) + Puppet::Parser::Resource.new("class", name, :scope => @scope, :source => mock('source')).evaluate + @scopes[name] = @scope.class_scope(klass) + @scopes[name].setvar("test", "value-#{name.sub(/.+::/,'')}") + end + end + { + "===${one::two::three::test}===" => "===value-three===", + "===$one::two::three::test===" => "===value-three===", + "===${one::two::test}===" => "===value-two===", + "===$one::two::test===" => "===value-two===", + "===${one::test}===" => "===value-one===", + "===$one::test===" => "===value-one===", + "===${::test}===" => "===value===", + "===$::test===" => "===value===" + }.each do |input, output| + it "should parse '#{input}' correctly" do + @scope.strinterp(input).should == output + end + end + end + + tests = { + "===${test}===" => "===value===", + "===${test} ${test} ${test}===" => "===value value value===", + "===$test ${test} $test===" => "===value value value===", + "===\\$test===" => "===$test===", + '===\\$test string===' => "===$test string===", + '===$test string===' => "===value string===", + '===a testing $===' => "===a testing $===", + '===a testing \$===' => "===a testing $===", + "===an escaped \\\n carriage return===" => "===an escaped carriage return===", + '\$' => "$", + '\s' => "\s", + '\t' => "\t", + '\n' => "\n" + } + + tests.each do |input, output| + it "should parse '#{input}' correctly" do + @scope.setvar("test", "value") + @scope.strinterp(input).should == output + end + end + + # #523 + %w{d f h l w z}.each do |l| + it "should parse '#{l}' when escaped" do + string = "\\" + l + @scope.strinterp(string).should == string + end + end + end + + def test_strinterp + # Make and evaluate our classes so the qualified lookups work + parser = mkparser + klass = parser.newclass("") + scope = mkscope(:parser => parser) + Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => scope, :source => mock('source')).evaluate + + assert_nothing_raised { + scope.setvar("test","value") + } + + scopes = {"" => scope} + + %w{one one::two one::two::three}.each do |name| + klass = parser.newclass(name) + Puppet::Parser::Resource.new(:type => "class", :title => name, :scope => scope, :source => mock('source')).evaluate + scopes[name] = scope.class_scope(klass) + scopes[name].setvar("test", "value-%s" % name.sub(/.+::/,'')) + end + + assert_equal("value", scope.lookupvar("::test"), "did not look up qualified value correctly") + tests.each do |input, output| + assert_nothing_raised("Failed to scan %s" % input.inspect) do + assert_equal(output, scope.strinterp(input), + 'did not parserret %s correctly' % input.inspect) + end + end + + logs = [] + Puppet::Util::Log.close + Puppet::Util::Log.newdestination(logs) + + # #523 + %w{d f h l w z}.each do |l| + string = "\\" + l + assert_nothing_raised do + assert_equal(string, scope.strinterp(string), + 'did not parserret %s correctly' % string) + end + + assert(logs.detect { |m| m.message =~ /Unrecognised escape/ }, + "Did not get warning about escape sequence with %s" % string) + logs.clear + end + end + + describe "when setting ephemeral vars from matches" do + before :each do + @match = stub 'match', :is_a? => true + @match.stubs(:[]).with(0).returns("this is a string") + @match.stubs(:captures).returns([]) + @scope.stubs(:setvar) + end + + it "should accept only MatchData" do + lambda { @scope.ephemeral_from("match") }.should raise_error + end + + it "should set $0 with the full match" do + @scope.expects(:setvar).with { |*arg| arg[0] == "0" and arg[1] == "this is a string" and arg[2][:ephemeral] } + + @scope.ephemeral_from(@match) + end + + it "should set every capture as ephemeral var" do + @match.stubs(:captures).returns([:capture1,:capture2]) + @scope.expects(:setvar).with { |*arg| arg[0] == "1" and arg[1] == :capture1 and arg[2][:ephemeral] } + @scope.expects(:setvar).with { |*arg| arg[0] == "2" and arg[1] == :capture2 and arg[2][:ephemeral] } + + @scope.ephemeral_from(@match) + end + + it "should create a new ephemeral level" do + @scope.expects(:new_ephemeral) + @scope.ephemeral_from(@match) + end + end + + describe "when unsetting variables" do + it "should be able to unset normal variables" do + @scope.setvar("foo", "bar") + @scope.unsetvar("foo") + @scope.lookupvar("foo").should == "" + end + + it "should be able to unset ephemeral variables" do + @scope.setvar("0", "bar", :ephemeral => true) + @scope.unsetvar("0") + @scope.lookupvar("0").should == "" + end + + it "should not unset ephemeral variables in previous ephemeral scope" do + @scope.setvar("0", "bar", :ephemeral => true) + @scope.new_ephemeral + @scope.unsetvar("0") + @scope.lookupvar("0").should == "bar" + end + end + + it "should use its namespaces to find hostclasses" do + klass = @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "a::b::c") + @scope.add_namespace "a::b" + @scope.find_hostclass("c").should equal(klass) + end + + it "should use its namespaces to find definitions" do + define = @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "a::b::c") + @scope.add_namespace "a::b" + @scope.find_definition("c").should equal(define) + end + + describe "when managing defaults" do + it "should be able to set and lookup defaults" do + param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) + @scope.setdefaults(:mytype, param) + @scope.lookupdefaults(:mytype).should == {:myparam => param} + end + + it "should fail if a default is already defined and a new default is being defined" do + param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) + @scope.setdefaults(:mytype, param) + lambda { @scope.setdefaults(:mytype, param) }.should raise_error(Puppet::ParseError) + end + + it "should return multiple defaults at once" do + param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) + @scope.setdefaults(:mytype, param1) + param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => "myvalue", :source => stub("source")) + @scope.setdefaults(:mytype, param2) + + @scope.lookupdefaults(:mytype).should == {:myparam => param1, :other => param2} + end + + it "should look up defaults defined in parent scopes" do + param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) + @scope.setdefaults(:mytype, param1) + + child_scope = @scope.newscope + param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => "myvalue", :source => stub("source")) + child_scope.setdefaults(:mytype, param2) + + child_scope.lookupdefaults(:mytype).should == {:myparam => param1, :other => param2} + end + end +end diff --git a/spec/unit/parser/scope_spec_spec.rb b/spec/unit/parser/scope_spec_spec.rb deleted file mode 100755 index 29dca38f4..000000000 --- a/spec/unit/parser/scope_spec_spec.rb +++ /dev/null @@ -1,624 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Parser::Scope do - before :each do - @topscope = Puppet::Parser::Scope.new() - # This is necessary so we don't try to use the compiler to discover our parent. - @topscope.parent = nil - @scope = Puppet::Parser::Scope.new() - @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) - @scope.parent = @topscope - end - - it "should be able to store references to class scopes" do - lambda { @scope.class_set "myname", "myscope" }.should_not raise_error - end - - it "should be able to retrieve class scopes by name" do - @scope.class_set "myname", "myscope" - @scope.class_scope("myname").should == "myscope" - end - - it "should be able to retrieve class scopes by object" do - klass = mock 'ast_class' - klass.expects(:name).returns("myname") - @scope.class_set "myname", "myscope" - @scope.class_scope(klass).should == "myscope" - end - - it "should be able to retrieve its parent module name from the source of its parent type" do - @topscope.source = Puppet::Resource::Type.new(:hostclass, :foo) - @topscope.source.module_name = "foo" - - @scope.parent_module_name.should == "foo" - end - - it "should return a nil parent module name if it has no parent" do - @topscope.parent_module_name.should be_nil - end - - it "should return a nil parent module name if its parent has no source" do - @scope.parent_module_name.should be_nil - end - - it "should get its environment from its compiler" do - env = stub 'environment' - compiler = stub 'compiler', :environment => env - scope = Puppet::Parser::Scope.new :compiler => compiler - scope.environment.should equal(env) - end - - it "should use the resource type collection helper to find its known resource types" do - Puppet::Parser::Scope.ancestors.should include(Puppet::Resource::TypeCollectionHelper) - end - - describe "when initializing" do - it "should extend itself with its environment's Functions module as well as the default" do - env = Puppet::Node::Environment.new("myenv") - compiler = stub 'compiler', :environment => env - mod = Module.new - root_mod = Module.new - Puppet::Parser::Functions.expects(:environment_module).with(Puppet::Node::Environment.root).returns root_mod - Puppet::Parser::Functions.expects(:environment_module).with(env).returns mod - - Puppet::Parser::Scope.new(:compiler => compiler).singleton_class.ancestors.should be_include(mod) - end - - it "should extend itself with the default Functions module if it has no environment" do - mod = Module.new - Puppet::Parser::Functions.expects(:environment_module).with(Puppet::Node::Environment.root).returns(mod) - - Puppet::Parser::Functions.expects(:environment_module).with(nil).returns mod - - Puppet::Parser::Scope.new().singleton_class.ancestors.should be_include(mod) - end - end - - describe "when looking up a variable" do - it "should default to an empty string" do - @scope.lookupvar("var").should == "" - end - - it "should return an string when asked for a string" do - @scope.lookupvar("var", true).should == "" - end - - it "should return ':undefined' for unset variables when asked not to return a string" do - @scope.lookupvar("var", false).should == :undefined - end - - it "should be able to look up values" do - @scope.setvar("var", "yep") - @scope.lookupvar("var").should == "yep" - end - - it "should be able to look up hashes" do - @scope.setvar("var", {"a" => "b"}) - @scope.lookupvar("var").should == {"a" => "b"} - end - - it "should be able to look up variables in parent scopes" do - @topscope.setvar("var", "parentval") - @scope.lookupvar("var").should == "parentval" - end - - it "should prefer its own values to parent values" do - @topscope.setvar("var", "parentval") - @scope.setvar("var", "childval") - @scope.lookupvar("var").should == "childval" - end - - describe "and the variable is qualified" do - before do - @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foonode")) - @scope.compiler = @compiler - @known_resource_types = @scope.known_resource_types - end - - def newclass(name) - @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) - end - - def create_class_scope(name) - klass = newclass(name) - Puppet::Parser::Resource.new("class", name, :scope => @scope, :source => mock('source')).evaluate - - return @scope.class_scope(klass) - end - - it "should be able to look up explicitly fully qualified variables from main" do - other_scope = create_class_scope("") - - other_scope.setvar("othervar", "otherval") - - @scope.lookupvar("::othervar").should == "otherval" - end - - it "should be able to look up explicitly fully qualified variables from other scopes" do - other_scope = create_class_scope("other") - - other_scope.setvar("var", "otherval") - - @scope.lookupvar("::other::var").should == "otherval" - end - - it "should be able to look up deeply qualified variables" do - other_scope = create_class_scope("other::deep::klass") - - other_scope.setvar("var", "otherval") - - @scope.lookupvar("other::deep::klass::var").should == "otherval" - end - - it "should return an empty string for qualified variables that cannot be found in other classes" do - other_scope = create_class_scope("other::deep::klass") - - @scope.lookupvar("other::deep::klass::var").should == "" - end - - it "should warn and return an empty string for qualified variables whose classes have not been evaluated" do - klass = newclass("other::deep::klass") - @scope.expects(:warning) - @scope.lookupvar("other::deep::klass::var").should == "" - end - - it "should warn and return an empty string for qualified variables whose classes do not exist" do - @scope.expects(:warning) - @scope.lookupvar("other::deep::klass::var").should == "" - end - - it "should return ':undefined' when asked for a non-string qualified variable from a class that does not exist" do - @scope.stubs(:warning) - @scope.lookupvar("other::deep::klass::var", false).should == :undefined - end - - it "should return ':undefined' when asked for a non-string qualified variable from a class that has not been evaluated" do - @scope.stubs(:warning) - klass = newclass("other::deep::klass") - @scope.lookupvar("other::deep::klass::var", false).should == :undefined - end - end - end - - describe "when setvar is called with append=true" do - it "should raise error if the variable is already defined in this scope" do - @scope.setvar("var","1", :append => false) - lambda { @scope.setvar("var","1", :append => true) }.should raise_error(Puppet::ParseError) - end - - it "should lookup current variable value" do - @scope.expects(:lookupvar).with("var").returns("2") - @scope.setvar("var","1", :append => true) - end - - it "should store the concatenated string '42'" do - @topscope.setvar("var","4", :append => false) - @scope.setvar("var","2", :append => true) - @scope.lookupvar("var").should == "42" - end - - it "should store the concatenated array [4,2]" do - @topscope.setvar("var",[4], :append => false) - @scope.setvar("var",[2], :append => true) - @scope.lookupvar("var").should == [4,2] - end - - it "should store the merged hash {a => b, c => d}" do - @topscope.setvar("var",{"a" => "b"}, :append => false) - @scope.setvar("var",{"c" => "d"}, :append => true) - @scope.lookupvar("var").should == {"a" => "b", "c" => "d"} - end - - it "should raise an error when appending a hash with something other than another hash" do - @topscope.setvar("var",{"a" => "b"}, :append => false) - lambda { @scope.setvar("var","not a hash", :append => true) }.should raise_error - end - end - - describe "when calling number?" do - it "should return nil if called with anything not a number" do - Puppet::Parser::Scope.number?([2]).should be_nil - end - - it "should return a Fixnum for a Fixnum" do - Puppet::Parser::Scope.number?(2).should be_an_instance_of(Fixnum) - end - - it "should return a Float for a Float" do - Puppet::Parser::Scope.number?(2.34).should be_an_instance_of(Float) - end - - it "should return 234 for '234'" do - Puppet::Parser::Scope.number?("234").should == 234 - end - - it "should return nil for 'not a number'" do - Puppet::Parser::Scope.number?("not a number").should be_nil - end - - it "should return 23.4 for '23.4'" do - Puppet::Parser::Scope.number?("23.4").should == 23.4 - end - - it "should return 23.4e13 for '23.4e13'" do - Puppet::Parser::Scope.number?("23.4e13").should == 23.4e13 - end - - it "should understand negative numbers" do - Puppet::Parser::Scope.number?("-234").should == -234 - end - - it "should know how to convert exponential float numbers ala '23e13'" do - Puppet::Parser::Scope.number?("23e13").should == 23e13 - end - - it "should understand hexadecimal numbers" do - Puppet::Parser::Scope.number?("0x234").should == 0x234 - end - - it "should understand octal numbers" do - Puppet::Parser::Scope.number?("0755").should == 0755 - end - - it "should return nil on malformed integers" do - Puppet::Parser::Scope.number?("0.24.5").should be_nil - end - - it "should convert strings with leading 0 to integer if they are not octal" do - Puppet::Parser::Scope.number?("0788").should == 788 - end - - it "should convert strings of negative integers" do - Puppet::Parser::Scope.number?("-0788").should == -788 - end - - it "should return nil on malformed hexadecimal numbers" do - Puppet::Parser::Scope.number?("0x89g").should be_nil - end - end - - describe "when using ephemeral variables" do - it "should store the variable value" do - @scope.setvar("1", :value, :ephemeral => true) - - @scope.lookupvar("1").should == :value - end - - it "should remove the variable value when unset_ephemeral_var is called" do - @scope.setvar("1", :value, :ephemeral => true) - @scope.stubs(:parent).returns(nil) - - @scope.unset_ephemeral_var - - @scope.lookupvar("1", false).should == :undefined - end - - it "should not remove classic variables when unset_ephemeral_var is called" do - @scope.setvar("myvar", :value1) - @scope.setvar("1", :value2, :ephemeral => true) - @scope.stubs(:parent).returns(nil) - - @scope.unset_ephemeral_var - - @scope.lookupvar("myvar", false).should == :value1 - end - - it "should raise an error when setting it again" do - @scope.setvar("1", :value2, :ephemeral => true) - lambda { @scope.setvar("1", :value3, :ephemeral => true) }.should raise_error - end - - it "should declare ephemeral number only variable names" do - @scope.ephemeral?("0").should be_true - end - - it "should not declare ephemeral other variable names" do - @scope.ephemeral?("abc0").should be_nil - end - - describe "with more than one level" do - it "should prefer latest ephemeral scopes" do - @scope.setvar("0", :earliest, :ephemeral => true) - @scope.new_ephemeral - @scope.setvar("0", :latest, :ephemeral => true) - @scope.lookupvar("0", false).should == :latest - end - - it "should be able to report the current level" do - @scope.ephemeral_level.should == 1 - @scope.new_ephemeral - @scope.ephemeral_level.should == 2 - end - - it "should check presence of an ephemeral variable accross multiple levels" do - @scope.new_ephemeral - @scope.setvar("1", :value1, :ephemeral => true) - @scope.new_ephemeral - @scope.setvar("0", :value2, :ephemeral => true) - @scope.new_ephemeral - @scope.ephemeral_include?("1").should be_true - end - - it "should return false when an ephemeral variable doesn't exist in any ephemeral scope" do - @scope.new_ephemeral - @scope.setvar("1", :value1, :ephemeral => true) - @scope.new_ephemeral - @scope.setvar("0", :value2, :ephemeral => true) - @scope.new_ephemeral - @scope.ephemeral_include?("2").should be_false - end - - it "should get ephemeral values from earlier scope when not in later" do - @scope.setvar("1", :value1, :ephemeral => true) - @scope.new_ephemeral - @scope.setvar("0", :value2, :ephemeral => true) - @scope.lookupvar("1", false).should == :value1 - end - - describe "when calling unset_ephemeral_var without a level" do - it "should remove all the variables values" do - @scope.setvar("1", :value1, :ephemeral => true) - @scope.new_ephemeral - @scope.setvar("1", :value2, :ephemeral => true) - - @scope.unset_ephemeral_var - - @scope.lookupvar("1", false).should == :undefined - end - end - - describe "when calling unset_ephemeral_var with a level" do - it "should remove ephemeral scopes up to this level" do - @scope.setvar("1", :value1, :ephemeral => true) - @scope.new_ephemeral - @scope.setvar("1", :value2, :ephemeral => true) - @scope.new_ephemeral - @scope.setvar("1", :value3, :ephemeral => true) - - @scope.unset_ephemeral_var(2) - - @scope.lookupvar("1", false).should == :value2 - end - end - end - end - - describe "when interpolating string" do - (0..9).each do |n| - it "should allow $#{n} to match" do - @scope.setvar(n.to_s, "value", :ephemeral => true) - - @scope.strinterp("$#{n}").should == "value" - end - end - - (0..9).each do |n| - it "should not allow $#{n} to match if not ephemeral" do - @scope.setvar(n.to_s, "value", :ephemeral => false) - - @scope.strinterp("$#{n}").should_not == "value" - end - end - - it "should not allow $10 to match" do - @scope.setvar("10", "value", :ephemeral => true) - - @scope.strinterp('==$10==').should_not == "==value==" - end - - it "should not allow ${10} to match" do - @scope.setvar("10", "value", :ephemeral => true) - - @scope.strinterp('==${10}==').should == "==value==" - end - - describe "with qualified variables" do - before do - @scopes = {} - klass = @scope.known_resource_types.add(Puppet::Resource::Type.new(:hostclass, "")) - Puppet::Parser::Resource.new("class", :main, :scope => @scope, :source => mock('source')).evaluate - @scopes[""] = @scope.class_scope(klass) - @scopes[""].setvar("test", "value") - - %w{one one::two one::two::three}.each do |name| - klass = @scope.known_resource_types.add(Puppet::Resource::Type.new(:hostclass, name)) - Puppet::Parser::Resource.new("class", name, :scope => @scope, :source => mock('source')).evaluate - @scopes[name] = @scope.class_scope(klass) - @scopes[name].setvar("test", "value-#{name.sub(/.+::/,'')}") - end - end - { - "===${one::two::three::test}===" => "===value-three===", - "===$one::two::three::test===" => "===value-three===", - "===${one::two::test}===" => "===value-two===", - "===$one::two::test===" => "===value-two===", - "===${one::test}===" => "===value-one===", - "===$one::test===" => "===value-one===", - "===${::test}===" => "===value===", - "===$::test===" => "===value===" - }.each do |input, output| - it "should parse '#{input}' correctly" do - @scope.strinterp(input).should == output - end - end - end - - tests = { - "===${test}===" => "===value===", - "===${test} ${test} ${test}===" => "===value value value===", - "===$test ${test} $test===" => "===value value value===", - "===\\$test===" => "===$test===", - '===\\$test string===' => "===$test string===", - '===$test string===' => "===value string===", - '===a testing $===' => "===a testing $===", - '===a testing \$===' => "===a testing $===", - "===an escaped \\\n carriage return===" => "===an escaped carriage return===", - '\$' => "$", - '\s' => "\s", - '\t' => "\t", - '\n' => "\n" - } - - tests.each do |input, output| - it "should parse '#{input}' correctly" do - @scope.setvar("test", "value") - @scope.strinterp(input).should == output - end - end - - # #523 - %w{d f h l w z}.each do |l| - it "should parse '#{l}' when escaped" do - string = "\\" + l - @scope.strinterp(string).should == string - end - end - end - - def test_strinterp - # Make and evaluate our classes so the qualified lookups work - parser = mkparser - klass = parser.newclass("") - scope = mkscope(:parser => parser) - Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => scope, :source => mock('source')).evaluate - - assert_nothing_raised { - scope.setvar("test","value") - } - - scopes = {"" => scope} - - %w{one one::two one::two::three}.each do |name| - klass = parser.newclass(name) - Puppet::Parser::Resource.new(:type => "class", :title => name, :scope => scope, :source => mock('source')).evaluate - scopes[name] = scope.class_scope(klass) - scopes[name].setvar("test", "value-%s" % name.sub(/.+::/,'')) - end - - assert_equal("value", scope.lookupvar("::test"), "did not look up qualified value correctly") - tests.each do |input, output| - assert_nothing_raised("Failed to scan %s" % input.inspect) do - assert_equal(output, scope.strinterp(input), - 'did not parserret %s correctly' % input.inspect) - end - end - - logs = [] - Puppet::Util::Log.close - Puppet::Util::Log.newdestination(logs) - - # #523 - %w{d f h l w z}.each do |l| - string = "\\" + l - assert_nothing_raised do - assert_equal(string, scope.strinterp(string), - 'did not parserret %s correctly' % string) - end - - assert(logs.detect { |m| m.message =~ /Unrecognised escape/ }, - "Did not get warning about escape sequence with %s" % string) - logs.clear - end - end - - describe "when setting ephemeral vars from matches" do - before :each do - @match = stub 'match', :is_a? => true - @match.stubs(:[]).with(0).returns("this is a string") - @match.stubs(:captures).returns([]) - @scope.stubs(:setvar) - end - - it "should accept only MatchData" do - lambda { @scope.ephemeral_from("match") }.should raise_error - end - - it "should set $0 with the full match" do - @scope.expects(:setvar).with { |*arg| arg[0] == "0" and arg[1] == "this is a string" and arg[2][:ephemeral] } - - @scope.ephemeral_from(@match) - end - - it "should set every capture as ephemeral var" do - @match.stubs(:captures).returns([:capture1,:capture2]) - @scope.expects(:setvar).with { |*arg| arg[0] == "1" and arg[1] == :capture1 and arg[2][:ephemeral] } - @scope.expects(:setvar).with { |*arg| arg[0] == "2" and arg[1] == :capture2 and arg[2][:ephemeral] } - - @scope.ephemeral_from(@match) - end - - it "should create a new ephemeral level" do - @scope.expects(:new_ephemeral) - @scope.ephemeral_from(@match) - end - end - - describe "when unsetting variables" do - it "should be able to unset normal variables" do - @scope.setvar("foo", "bar") - @scope.unsetvar("foo") - @scope.lookupvar("foo").should == "" - end - - it "should be able to unset ephemeral variables" do - @scope.setvar("0", "bar", :ephemeral => true) - @scope.unsetvar("0") - @scope.lookupvar("0").should == "" - end - - it "should not unset ephemeral variables in previous ephemeral scope" do - @scope.setvar("0", "bar", :ephemeral => true) - @scope.new_ephemeral - @scope.unsetvar("0") - @scope.lookupvar("0").should == "bar" - end - end - - it "should use its namespaces to find hostclasses" do - klass = @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "a::b::c") - @scope.add_namespace "a::b" - @scope.find_hostclass("c").should equal(klass) - end - - it "should use its namespaces to find definitions" do - define = @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "a::b::c") - @scope.add_namespace "a::b" - @scope.find_definition("c").should equal(define) - end - - describe "when managing defaults" do - it "should be able to set and lookup defaults" do - param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) - @scope.setdefaults(:mytype, param) - @scope.lookupdefaults(:mytype).should == {:myparam => param} - end - - it "should fail if a default is already defined and a new default is being defined" do - param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) - @scope.setdefaults(:mytype, param) - lambda { @scope.setdefaults(:mytype, param) }.should raise_error(Puppet::ParseError) - end - - it "should return multiple defaults at once" do - param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) - @scope.setdefaults(:mytype, param1) - param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => "myvalue", :source => stub("source")) - @scope.setdefaults(:mytype, param2) - - @scope.lookupdefaults(:mytype).should == {:myparam => param1, :other => param2} - end - - it "should look up defaults defined in parent scopes" do - param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) - @scope.setdefaults(:mytype, param1) - - child_scope = @scope.newscope - param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => "myvalue", :source => stub("source")) - child_scope.setdefaults(:mytype, param2) - - child_scope.lookupdefaults(:mytype).should == {:myparam => param1, :other => param2} - end - end -end diff --git a/spec/unit/parser/templatewrapper_spec.rb b/spec/unit/parser/templatewrapper_spec.rb new file mode 100755 index 000000000..1b4121643 --- /dev/null +++ b/spec/unit/parser/templatewrapper_spec.rb @@ -0,0 +1,144 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Parser::TemplateWrapper do + before(:each) do + @known_resource_types = Puppet::Resource::TypeCollection.new("env") + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) + @compiler.environment.stubs(:known_resource_types).returns @known_resource_types + @scope = Puppet::Parser::Scope.new :compiler => @compiler + + @file = "fake_template" + Puppet::Parser::Files.stubs(:find_template).returns("/tmp/fake_template") + FileTest.stubs(:exists?).returns("true") + File.stubs(:read).with("/tmp/fake_template").returns("template content") + @tw = Puppet::Parser::TemplateWrapper.new(@scope) + end + + it "should create a new object TemplateWrapper from a scope" do + tw = Puppet::Parser::TemplateWrapper.new(@scope) + + tw.should be_a_kind_of(Puppet::Parser::TemplateWrapper) + end + + it "should check template file existance and read its content" do + Puppet::Parser::Files.expects(:find_template).with("fake_template", @scope.environment.to_s).returns("/tmp/fake_template") + File.expects(:read).with("/tmp/fake_template").returns("template content") + + @tw.file = @file + end + + it "should mark the file for watching" do + Puppet::Parser::Files.expects(:find_template).returns("/tmp/fake_template") + File.stubs(:read) + + @known_resource_types.expects(:watch_file).with("/tmp/fake_template") + @tw.file = @file + end + + it "should fail if a template cannot be found" do + Puppet::Parser::Files.expects(:find_template).returns nil + + lambda { @tw.file = @file }.should raise_error(Puppet::ParseError) + end + + it "should turn into a string like template[name] for file based template" do + @tw.file = @file + @tw.to_s.should eql("template[/tmp/fake_template]") + end + + it "should turn into a string like template[inline] for string-based template" do + @tw.to_s.should eql("template[inline]") + end + + it "should return the processed template contents with a call to result" do + template_mock = mock("template", :result => "woot!") + File.expects(:read).with("/tmp/fake_template").returns("template contents") + ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) + + @tw.file = @file + @tw.result.should eql("woot!") + end + + it "should return the processed template contents with a call to result and a string" do + template_mock = mock("template", :result => "woot!") + ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) + + @tw.result("template contents").should eql("woot!") + end + + it "should return the contents of a variable if called via method_missing" do + @scope.expects(:lookupvar).with("chicken", false).returns("is good") + tw = Puppet::Parser::TemplateWrapper.new(@scope) + tw.chicken.should eql("is good") + end + + it "should throw an exception if a variable is called via method_missing and it does not exist" do + @scope.expects(:lookupvar).with("chicken", false).returns(:undefined) + tw = Puppet::Parser::TemplateWrapper.new(@scope) + lambda { tw.chicken }.should raise_error(Puppet::ParseError) + end + + it "should allow you to check whether a variable is defined with has_variable?" do + @scope.expects(:lookupvar).with("chicken", false).returns("is good") + tw = Puppet::Parser::TemplateWrapper.new(@scope) + tw.has_variable?("chicken").should eql(true) + end + + it "should allow you to check whether a variable is not defined with has_variable?" do + @scope.expects(:lookupvar).with("chicken", false).returns(:undefined) + tw = Puppet::Parser::TemplateWrapper.new(@scope) + tw.has_variable?("chicken").should eql(false) + end + + it "should allow you to retrieve the defined classes with classes" do + catalog = mock 'catalog', :classes => ["class1", "class2"] + @scope.expects(:catalog).returns( catalog ) + tw = Puppet::Parser::TemplateWrapper.new(@scope) + tw.classes().should == ["class1", "class2"] + end + + it "should allow you to retrieve all the tags with all_tags" do + catalog = mock 'catalog', :tags => ["tag1", "tag2"] + @scope.expects(:catalog).returns( catalog ) + tw = Puppet::Parser::TemplateWrapper.new(@scope) + tw.all_tags().should == ["tag1","tag2"] + end + + it "should allow you to retrieve the tags defined in the current scope" do + @scope.expects(:tags).returns( ["tag1", "tag2"] ) + tw = Puppet::Parser::TemplateWrapper.new(@scope) + tw.tags().should == ["tag1","tag2"] + end + + it "should set all of the scope's variables as instance variables" do + template_mock = mock("template", :result => "woot!") + ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) + + @scope.expects(:to_hash).returns("one" => "foo") + @tw.result("template contents") + + @tw.instance_variable_get("@one").should == "foo" + end + + it "should not error out if one of the variables is a symbol" do + template_mock = mock("template", :result => "woot!") + ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) + + @scope.expects(:to_hash).returns(:_timestamp => "1234") + @tw.result("template contents") + end + + %w{! . ; :}.each do |badchar| + it "should translate #{badchar} to _ when setting the instance variables" do + template_mock = mock("template", :result => "woot!") + ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) + + @scope.expects(:to_hash).returns("one#{badchar}" => "foo") + @tw.result("template contents") + + @tw.instance_variable_get("@one_").should == "foo" + end + end +end diff --git a/spec/unit/parser/templatewrapper_spec_spec.rb b/spec/unit/parser/templatewrapper_spec_spec.rb deleted file mode 100755 index 1b4121643..000000000 --- a/spec/unit/parser/templatewrapper_spec_spec.rb +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Parser::TemplateWrapper do - before(:each) do - @known_resource_types = Puppet::Resource::TypeCollection.new("env") - @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) - @compiler.environment.stubs(:known_resource_types).returns @known_resource_types - @scope = Puppet::Parser::Scope.new :compiler => @compiler - - @file = "fake_template" - Puppet::Parser::Files.stubs(:find_template).returns("/tmp/fake_template") - FileTest.stubs(:exists?).returns("true") - File.stubs(:read).with("/tmp/fake_template").returns("template content") - @tw = Puppet::Parser::TemplateWrapper.new(@scope) - end - - it "should create a new object TemplateWrapper from a scope" do - tw = Puppet::Parser::TemplateWrapper.new(@scope) - - tw.should be_a_kind_of(Puppet::Parser::TemplateWrapper) - end - - it "should check template file existance and read its content" do - Puppet::Parser::Files.expects(:find_template).with("fake_template", @scope.environment.to_s).returns("/tmp/fake_template") - File.expects(:read).with("/tmp/fake_template").returns("template content") - - @tw.file = @file - end - - it "should mark the file for watching" do - Puppet::Parser::Files.expects(:find_template).returns("/tmp/fake_template") - File.stubs(:read) - - @known_resource_types.expects(:watch_file).with("/tmp/fake_template") - @tw.file = @file - end - - it "should fail if a template cannot be found" do - Puppet::Parser::Files.expects(:find_template).returns nil - - lambda { @tw.file = @file }.should raise_error(Puppet::ParseError) - end - - it "should turn into a string like template[name] for file based template" do - @tw.file = @file - @tw.to_s.should eql("template[/tmp/fake_template]") - end - - it "should turn into a string like template[inline] for string-based template" do - @tw.to_s.should eql("template[inline]") - end - - it "should return the processed template contents with a call to result" do - template_mock = mock("template", :result => "woot!") - File.expects(:read).with("/tmp/fake_template").returns("template contents") - ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) - - @tw.file = @file - @tw.result.should eql("woot!") - end - - it "should return the processed template contents with a call to result and a string" do - template_mock = mock("template", :result => "woot!") - ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) - - @tw.result("template contents").should eql("woot!") - end - - it "should return the contents of a variable if called via method_missing" do - @scope.expects(:lookupvar).with("chicken", false).returns("is good") - tw = Puppet::Parser::TemplateWrapper.new(@scope) - tw.chicken.should eql("is good") - end - - it "should throw an exception if a variable is called via method_missing and it does not exist" do - @scope.expects(:lookupvar).with("chicken", false).returns(:undefined) - tw = Puppet::Parser::TemplateWrapper.new(@scope) - lambda { tw.chicken }.should raise_error(Puppet::ParseError) - end - - it "should allow you to check whether a variable is defined with has_variable?" do - @scope.expects(:lookupvar).with("chicken", false).returns("is good") - tw = Puppet::Parser::TemplateWrapper.new(@scope) - tw.has_variable?("chicken").should eql(true) - end - - it "should allow you to check whether a variable is not defined with has_variable?" do - @scope.expects(:lookupvar).with("chicken", false).returns(:undefined) - tw = Puppet::Parser::TemplateWrapper.new(@scope) - tw.has_variable?("chicken").should eql(false) - end - - it "should allow you to retrieve the defined classes with classes" do - catalog = mock 'catalog', :classes => ["class1", "class2"] - @scope.expects(:catalog).returns( catalog ) - tw = Puppet::Parser::TemplateWrapper.new(@scope) - tw.classes().should == ["class1", "class2"] - end - - it "should allow you to retrieve all the tags with all_tags" do - catalog = mock 'catalog', :tags => ["tag1", "tag2"] - @scope.expects(:catalog).returns( catalog ) - tw = Puppet::Parser::TemplateWrapper.new(@scope) - tw.all_tags().should == ["tag1","tag2"] - end - - it "should allow you to retrieve the tags defined in the current scope" do - @scope.expects(:tags).returns( ["tag1", "tag2"] ) - tw = Puppet::Parser::TemplateWrapper.new(@scope) - tw.tags().should == ["tag1","tag2"] - end - - it "should set all of the scope's variables as instance variables" do - template_mock = mock("template", :result => "woot!") - ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) - - @scope.expects(:to_hash).returns("one" => "foo") - @tw.result("template contents") - - @tw.instance_variable_get("@one").should == "foo" - end - - it "should not error out if one of the variables is a symbol" do - template_mock = mock("template", :result => "woot!") - ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) - - @scope.expects(:to_hash).returns(:_timestamp => "1234") - @tw.result("template contents") - end - - %w{! . ; :}.each do |badchar| - it "should translate #{badchar} to _ when setting the instance variables" do - template_mock = mock("template", :result => "woot!") - ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) - - @scope.expects(:to_hash).returns("one#{badchar}" => "foo") - @tw.result("template contents") - - @tw.instance_variable_get("@one_").should == "foo" - end - end -end diff --git a/spec/unit/parser/type_loader_spec.rb b/spec/unit/parser/type_loader_spec.rb new file mode 100644 index 000000000..2cd837c78 --- /dev/null +++ b/spec/unit/parser/type_loader_spec.rb @@ -0,0 +1,201 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/parser/type_loader' +require 'puppet_spec/files' + +describe Puppet::Parser::TypeLoader do + include PuppetSpec::Files + + before do + @loader = Puppet::Parser::TypeLoader.new(:myenv) + end + + it "should support an environment" do + loader = Puppet::Parser::TypeLoader.new(:myenv) + loader.environment.name.should == :myenv + end + + it "should include the Environment Helper" do + @loader.class.ancestors.should be_include(Puppet::Node::Environment::Helper) + end + + it "should delegate its known resource types to its environment" do + @loader.known_resource_types.should be_instance_of(Puppet::Resource::TypeCollection) + end + + describe "when loading names from namespaces" do + it "should do nothing if the name to import is an empty string" do + @loader.expects(:name2files).never + @loader.load_until(["foo"], "") { |f| false }.should be_nil + end + + it "should turn the provided namespaces and name into a list of files" do + @loader.expects(:name2files).with(["foo"], "bar").returns [] + @loader.load_until(["foo"], "bar") { |f| false } + end + + it "should attempt to import each generated name" do + @loader.expects(:name2files).returns %w{foo bar} + @loader.expects(:import).with("foo") + @loader.expects(:import).with("bar") + @loader.load_until(["foo"], "bar") { |f| false } + end + + it "should yield after each import" do + yielded = [] + @loader.expects(:name2files).returns %w{foo bar} + @loader.expects(:import).with("foo") + @loader.expects(:import).with("bar") + @loader.load_until(["foo"], "bar") { |f| yielded << f; false } + yielded.should == %w{foo bar} + end + + it "should stop importing when the yielded block returns true" do + yielded = [] + @loader.expects(:name2files).returns %w{foo bar baz} + @loader.expects(:import).with("foo") + @loader.expects(:import).with("bar") + @loader.expects(:import).with("baz").never + @loader.load_until(["foo"], "bar") { |f| true if f == "bar" } + end + + it "should return the result of the block" do + yielded = [] + @loader.expects(:name2files).returns %w{foo bar baz} + @loader.expects(:import).with("foo") + @loader.expects(:import).with("bar") + @loader.expects(:import).with("baz").never + @loader.load_until(["foo"], "bar") { |f| 10 if f == "bar" }.should == 10 + end + + it "should return nil if the block never returns true" do + @loader.expects(:name2files).returns %w{foo bar} + @loader.expects(:import).with("foo") + @loader.expects(:import).with("bar") + @loader.load_until(["foo"], "bar") { |f| false }.should be_nil + end + + it "should know when a given name has been loaded" do + @loader.expects(:name2files).returns %w{file} + @loader.expects(:import).with("file") + @loader.load_until(["foo"], "bar") { |f| true } + @loader.should be_loaded("file") + end + + it "should set the module name on any created resource types" do + type = Puppet::Resource::Type.new(:hostclass, "mytype") + + Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}] + @loader.stubs(:parse_file) + @loader.load_until(["foo"], "one") { |f| type } + + type.module_name.should == "modname" + end + end + + describe "when mapping names to files" do + { + [["foo"], "::bar::baz"] => %w{bar/baz}, + [[""], "foo::bar"] => %w{foo foo/bar}, + [%w{foo}, "bar"] => %w{foo foo/bar bar}, + [%w{a b}, "bar"] => %w{a a/bar b b/bar bar}, + [%w{a::b::c}, "bar"] => %w{a a/b/c/bar bar}, + [%w{a::b}, "foo::bar"] => %w{a a/b/foo/bar foo/bar} + }.each do |inputs, outputs| + it "should produce #{outputs.inspect} from the #{inputs[0].inspect} namespace and #{inputs[1]} name" do + @loader.name2files(*inputs).should == outputs + end + end + end + + describe "when importing" do + before do + Puppet::Parser::Files.stubs(:find_manifests).returns ["modname", %w{file}] + @loader.stubs(:parse_file) + end + + it "should return immediately when imports are being ignored" do + Puppet::Parser::Files.expects(:find_manifests).never + Puppet[:ignoreimport] = true + @loader.import("foo").should be_nil + end + + it "should find all manifests matching the file or pattern" do + Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| pat == "myfile" }.returns ["modname", %w{one}] + @loader.import("myfile") + end + + it "should use the directory of the current file if one is set" do + Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:cwd] == "/current" }.returns ["modname", %w{one}] + @loader.import("myfile", "/current/file") + end + + it "should pass the environment when looking for files" do + Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:environment] == @loader.environment }.returns ["modname", %w{one}] + @loader.import("myfile") + end + + it "should fail if no files are found" do + Puppet::Parser::Files.expects(:find_manifests).returns [nil, []] + lambda { @loader.import("myfile") }.should raise_error(Puppet::ImportError) + end + + it "should parse each found file" do + Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] + @loader.expects(:parse_file).with("/one") + @loader.import("myfile") + end + + it "should make each file qualified before attempting to parse it" do + Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}] + @loader.expects(:parse_file).with("/current/one") + @loader.import("myfile", "/current/file") + end + + it "should know when a given file has been imported" do + Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] + @loader.import("myfile") + + @loader.should be_imported("/one") + end + + it "should not attempt to import files that have already been imported" do + Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] + @loader.expects(:parse_file).once + @loader.import("myfile") + + # This will fail if it tries to reimport the file. + @loader.import("myfile") + end + end + + describe "when parsing a file" do + before do + @parser = Puppet::Parser::Parser.new(@loader.environment) + @parser.stubs(:parse) + @parser.stubs(:file=) + Puppet::Parser::Parser.stubs(:new).with(@loader.environment).returns @parser + end + + it "should create a new parser instance for each file using the current environment" do + Puppet::Parser::Parser.expects(:new).with(@loader.environment).returns @parser + @loader.parse_file("/my/file") + end + + it "should assign the parser its file and parse" do + @parser.expects(:file=).with("/my/file") + @parser.expects(:parse) + @loader.parse_file("/my/file") + end + end + + it "should be able to add classes to the current resource type collection" do + file = tmpfile("simple_file") + File.open(file, "w") { |f| f.puts "class foo {}" } + @loader.import(file) + + @loader.known_resource_types.hostclass("foo").should be_instance_of(Puppet::Resource::Type) + end +end diff --git a/spec/unit/parser/type_loader_spec_spec.rb b/spec/unit/parser/type_loader_spec_spec.rb deleted file mode 100644 index 2cd837c78..000000000 --- a/spec/unit/parser/type_loader_spec_spec.rb +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/parser/type_loader' -require 'puppet_spec/files' - -describe Puppet::Parser::TypeLoader do - include PuppetSpec::Files - - before do - @loader = Puppet::Parser::TypeLoader.new(:myenv) - end - - it "should support an environment" do - loader = Puppet::Parser::TypeLoader.new(:myenv) - loader.environment.name.should == :myenv - end - - it "should include the Environment Helper" do - @loader.class.ancestors.should be_include(Puppet::Node::Environment::Helper) - end - - it "should delegate its known resource types to its environment" do - @loader.known_resource_types.should be_instance_of(Puppet::Resource::TypeCollection) - end - - describe "when loading names from namespaces" do - it "should do nothing if the name to import is an empty string" do - @loader.expects(:name2files).never - @loader.load_until(["foo"], "") { |f| false }.should be_nil - end - - it "should turn the provided namespaces and name into a list of files" do - @loader.expects(:name2files).with(["foo"], "bar").returns [] - @loader.load_until(["foo"], "bar") { |f| false } - end - - it "should attempt to import each generated name" do - @loader.expects(:name2files).returns %w{foo bar} - @loader.expects(:import).with("foo") - @loader.expects(:import).with("bar") - @loader.load_until(["foo"], "bar") { |f| false } - end - - it "should yield after each import" do - yielded = [] - @loader.expects(:name2files).returns %w{foo bar} - @loader.expects(:import).with("foo") - @loader.expects(:import).with("bar") - @loader.load_until(["foo"], "bar") { |f| yielded << f; false } - yielded.should == %w{foo bar} - end - - it "should stop importing when the yielded block returns true" do - yielded = [] - @loader.expects(:name2files).returns %w{foo bar baz} - @loader.expects(:import).with("foo") - @loader.expects(:import).with("bar") - @loader.expects(:import).with("baz").never - @loader.load_until(["foo"], "bar") { |f| true if f == "bar" } - end - - it "should return the result of the block" do - yielded = [] - @loader.expects(:name2files).returns %w{foo bar baz} - @loader.expects(:import).with("foo") - @loader.expects(:import).with("bar") - @loader.expects(:import).with("baz").never - @loader.load_until(["foo"], "bar") { |f| 10 if f == "bar" }.should == 10 - end - - it "should return nil if the block never returns true" do - @loader.expects(:name2files).returns %w{foo bar} - @loader.expects(:import).with("foo") - @loader.expects(:import).with("bar") - @loader.load_until(["foo"], "bar") { |f| false }.should be_nil - end - - it "should know when a given name has been loaded" do - @loader.expects(:name2files).returns %w{file} - @loader.expects(:import).with("file") - @loader.load_until(["foo"], "bar") { |f| true } - @loader.should be_loaded("file") - end - - it "should set the module name on any created resource types" do - type = Puppet::Resource::Type.new(:hostclass, "mytype") - - Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}] - @loader.stubs(:parse_file) - @loader.load_until(["foo"], "one") { |f| type } - - type.module_name.should == "modname" - end - end - - describe "when mapping names to files" do - { - [["foo"], "::bar::baz"] => %w{bar/baz}, - [[""], "foo::bar"] => %w{foo foo/bar}, - [%w{foo}, "bar"] => %w{foo foo/bar bar}, - [%w{a b}, "bar"] => %w{a a/bar b b/bar bar}, - [%w{a::b::c}, "bar"] => %w{a a/b/c/bar bar}, - [%w{a::b}, "foo::bar"] => %w{a a/b/foo/bar foo/bar} - }.each do |inputs, outputs| - it "should produce #{outputs.inspect} from the #{inputs[0].inspect} namespace and #{inputs[1]} name" do - @loader.name2files(*inputs).should == outputs - end - end - end - - describe "when importing" do - before do - Puppet::Parser::Files.stubs(:find_manifests).returns ["modname", %w{file}] - @loader.stubs(:parse_file) - end - - it "should return immediately when imports are being ignored" do - Puppet::Parser::Files.expects(:find_manifests).never - Puppet[:ignoreimport] = true - @loader.import("foo").should be_nil - end - - it "should find all manifests matching the file or pattern" do - Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| pat == "myfile" }.returns ["modname", %w{one}] - @loader.import("myfile") - end - - it "should use the directory of the current file if one is set" do - Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:cwd] == "/current" }.returns ["modname", %w{one}] - @loader.import("myfile", "/current/file") - end - - it "should pass the environment when looking for files" do - Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:environment] == @loader.environment }.returns ["modname", %w{one}] - @loader.import("myfile") - end - - it "should fail if no files are found" do - Puppet::Parser::Files.expects(:find_manifests).returns [nil, []] - lambda { @loader.import("myfile") }.should raise_error(Puppet::ImportError) - end - - it "should parse each found file" do - Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] - @loader.expects(:parse_file).with("/one") - @loader.import("myfile") - end - - it "should make each file qualified before attempting to parse it" do - Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}] - @loader.expects(:parse_file).with("/current/one") - @loader.import("myfile", "/current/file") - end - - it "should know when a given file has been imported" do - Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] - @loader.import("myfile") - - @loader.should be_imported("/one") - end - - it "should not attempt to import files that have already been imported" do - Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] - @loader.expects(:parse_file).once - @loader.import("myfile") - - # This will fail if it tries to reimport the file. - @loader.import("myfile") - end - end - - describe "when parsing a file" do - before do - @parser = Puppet::Parser::Parser.new(@loader.environment) - @parser.stubs(:parse) - @parser.stubs(:file=) - Puppet::Parser::Parser.stubs(:new).with(@loader.environment).returns @parser - end - - it "should create a new parser instance for each file using the current environment" do - Puppet::Parser::Parser.expects(:new).with(@loader.environment).returns @parser - @loader.parse_file("/my/file") - end - - it "should assign the parser its file and parse" do - @parser.expects(:file=).with("/my/file") - @parser.expects(:parse) - @loader.parse_file("/my/file") - end - end - - it "should be able to add classes to the current resource type collection" do - file = tmpfile("simple_file") - File.open(file, "w") { |f| f.puts "class foo {}" } - @loader.import(file) - - @loader.known_resource_types.hostclass("foo").should be_instance_of(Puppet::Resource::Type) - end -end diff --git a/spec/unit/property/ensure_spec.rb b/spec/unit/property/ensure_spec.rb new file mode 100644 index 000000000..aa31abbdc --- /dev/null +++ b/spec/unit/property/ensure_spec.rb @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/property/ensure' + +klass = Puppet::Property::Ensure + +describe klass do + it "should be a subclass of Property" do + klass.superclass.must == Puppet::Property + end +end diff --git a/spec/unit/property/ensure_spec_spec.rb b/spec/unit/property/ensure_spec_spec.rb deleted file mode 100644 index aa31abbdc..000000000 --- a/spec/unit/property/ensure_spec_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/property/ensure' - -klass = Puppet::Property::Ensure - -describe klass do - it "should be a subclass of Property" do - klass.superclass.must == Puppet::Property - end -end diff --git a/spec/unit/property/keyvalue_spec.rb b/spec/unit/property/keyvalue_spec.rb new file mode 100644 index 000000000..6f9eef531 --- /dev/null +++ b/spec/unit/property/keyvalue_spec.rb @@ -0,0 +1,168 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/property/keyvalue' + +klass = Puppet::Property::KeyValue + +describe klass do + + it "should be a subclass of Property" do + klass.superclass.must == Puppet::Property + end + + describe "as an instance" do + before do + # Wow that's a messy interface to the resource. + klass.initvars + @resource = stub 'resource', :[]= => nil, :property => nil + @property = klass.new(:resource => @resource) + end + + it "should have a , as default delimiter" do + @property.delimiter.should == ";" + end + + it "should have a = as default separator" do + @property.separator.should == "=" + end + + it "should have a :membership as default membership" do + @property.membership.should == :key_value_membership + end + + it "should return the same value passed into should_to_s" do + @property.should_to_s({:foo => "baz", :bar => "boo"}) == "foo=baz;bar=boo" + end + + it "should return the passed in array values joined with the delimiter from is_to_s" do + @property.is_to_s({"foo" => "baz" , "bar" => "boo"}).should == "foo=baz;bar=boo" + end + + describe "when calling inclusive?" do + it "should use the membership method to look up on the @resource" do + @property.expects(:membership).returns(:key_value_membership) + @resource.expects(:[]).with(:key_value_membership) + @property.inclusive? + end + + it "should return true when @resource[membership] == inclusive" do + @property.stubs(:membership).returns(:key_value_membership) + @resource.stubs(:[]).with(:key_value_membership).returns(:inclusive) + @property.inclusive?.must == true + end + + it "should return false when @resource[membership] != inclusive" do + @property.stubs(:membership).returns(:key_value_membership) + @resource.stubs(:[]).with(:key_value_membership).returns(:minimum) + @property.inclusive?.must == false + end + end + + describe "when calling process_current_hash" do + it "should return {} if hash is :absent" do + @property.process_current_hash(:absent).must == {} + end + + it "should set every key to nil if inclusive?" do + @property.stubs(:inclusive?).returns(true) + @property.process_current_hash({:foo => "bar", :do => "re"}).must == { :foo => nil, :do => nil } + end + + it "should return the hash if !inclusive?" do + @property.stubs(:inclusive?).returns(false) + @property.process_current_hash({:foo => "bar", :do => "re"}).must == {:foo => "bar", :do => "re"} + end + end + + describe "when calling should" do + it "should return nil if @should is nil" do + @property.should.must == nil + end + + it "should call process_current_hash" do + @property.should = ["foo=baz", "bar=boo"] + @property.stubs(:retrieve).returns({:do => "re", :mi => "fa" }) + @property.expects(:process_current_hash).returns({}) + @property.should + end + + it "should return the hashed values of @should and the nilled values of retrieve if inclusive" do + @property.should = ["foo=baz", "bar=boo"] + @property.expects(:retrieve).returns({:do => "re", :mi => "fa" }) + @property.expects(:inclusive?).returns(true) + @property.should.must == { :foo => "baz", :bar => "boo", :do => nil, :mi => nil } + end + + it "should return the hashed @should + the unique values of retrieve if !inclusive" do + @property.should = ["foo=baz", "bar=boo"] + @property.expects(:retrieve).returns({:foo => "diff", :do => "re", :mi => "fa"}) + @property.expects(:inclusive?).returns(false) + @property.should.must == { :foo => "baz", :bar => "boo", :do => "re", :mi => "fa" } + end + end + + describe "when calling retrieve" do + before do + @provider = mock("provider") + @property.stubs(:provider).returns(@provider) + end + + it "should send 'name' to the provider" do + @provider.expects(:send).with(:keys) + @property.expects(:name).returns(:keys) + @property.retrieve + end + + it "should return a hash with the provider returned info" do + @provider.stubs(:send).with(:keys).returns({"do" => "re", "mi" => "fa" }) + @property.stubs(:name).returns(:keys) + @property.retrieve == {"do" => "re", "mi" => "fa" } + end + + it "should return :absent when the provider returns :absent" do + @provider.stubs(:send).with(:keys).returns(:absent) + @property.stubs(:name).returns(:keys) + @property.retrieve == :absent + end + end + + describe "when calling hashify" do + it "should return the array hashified" do + @property.hashify(["foo=baz", "bar=boo"]).must == { :foo => "baz", :bar => "boo" } + end + end + + describe "when calling insync?" do + before do + @provider = mock("provider") + @property.stubs(:provider).returns(@provider) + @property.stubs(:name).returns(:prop_name) + end + + it "should return true unless @should is defined and not nil" do + @property.insync?("foo") == true + end + + it "should return true if the passed in values is nil" do + @property.should = "foo" + @property.insync?(nil) == true + end + + it "should return true if hashified should value == (retrieved) value passed in" do + @provider.stubs(:prop_name).returns({ :foo => "baz", :bar => "boo" }) + @property.should = ["foo=baz", "bar=boo"] + @property.expects(:inclusive?).returns(true) + @property.insync?({ :foo => "baz", :bar => "boo" }).must == true + end + + it "should return false if prepared value != should value" do + @provider.stubs(:prop_name).returns({ "foo" => "bee", "bar" => "boo" }) + @property.should = ["foo=baz", "bar=boo"] + @property.expects(:inclusive?).returns(true) + @property.insync?({ "foo" => "bee", "bar" => "boo" }).must == false + end + end + end +end diff --git a/spec/unit/property/keyvalue_spec_spec.rb b/spec/unit/property/keyvalue_spec_spec.rb deleted file mode 100644 index 6f9eef531..000000000 --- a/spec/unit/property/keyvalue_spec_spec.rb +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/property/keyvalue' - -klass = Puppet::Property::KeyValue - -describe klass do - - it "should be a subclass of Property" do - klass.superclass.must == Puppet::Property - end - - describe "as an instance" do - before do - # Wow that's a messy interface to the resource. - klass.initvars - @resource = stub 'resource', :[]= => nil, :property => nil - @property = klass.new(:resource => @resource) - end - - it "should have a , as default delimiter" do - @property.delimiter.should == ";" - end - - it "should have a = as default separator" do - @property.separator.should == "=" - end - - it "should have a :membership as default membership" do - @property.membership.should == :key_value_membership - end - - it "should return the same value passed into should_to_s" do - @property.should_to_s({:foo => "baz", :bar => "boo"}) == "foo=baz;bar=boo" - end - - it "should return the passed in array values joined with the delimiter from is_to_s" do - @property.is_to_s({"foo" => "baz" , "bar" => "boo"}).should == "foo=baz;bar=boo" - end - - describe "when calling inclusive?" do - it "should use the membership method to look up on the @resource" do - @property.expects(:membership).returns(:key_value_membership) - @resource.expects(:[]).with(:key_value_membership) - @property.inclusive? - end - - it "should return true when @resource[membership] == inclusive" do - @property.stubs(:membership).returns(:key_value_membership) - @resource.stubs(:[]).with(:key_value_membership).returns(:inclusive) - @property.inclusive?.must == true - end - - it "should return false when @resource[membership] != inclusive" do - @property.stubs(:membership).returns(:key_value_membership) - @resource.stubs(:[]).with(:key_value_membership).returns(:minimum) - @property.inclusive?.must == false - end - end - - describe "when calling process_current_hash" do - it "should return {} if hash is :absent" do - @property.process_current_hash(:absent).must == {} - end - - it "should set every key to nil if inclusive?" do - @property.stubs(:inclusive?).returns(true) - @property.process_current_hash({:foo => "bar", :do => "re"}).must == { :foo => nil, :do => nil } - end - - it "should return the hash if !inclusive?" do - @property.stubs(:inclusive?).returns(false) - @property.process_current_hash({:foo => "bar", :do => "re"}).must == {:foo => "bar", :do => "re"} - end - end - - describe "when calling should" do - it "should return nil if @should is nil" do - @property.should.must == nil - end - - it "should call process_current_hash" do - @property.should = ["foo=baz", "bar=boo"] - @property.stubs(:retrieve).returns({:do => "re", :mi => "fa" }) - @property.expects(:process_current_hash).returns({}) - @property.should - end - - it "should return the hashed values of @should and the nilled values of retrieve if inclusive" do - @property.should = ["foo=baz", "bar=boo"] - @property.expects(:retrieve).returns({:do => "re", :mi => "fa" }) - @property.expects(:inclusive?).returns(true) - @property.should.must == { :foo => "baz", :bar => "boo", :do => nil, :mi => nil } - end - - it "should return the hashed @should + the unique values of retrieve if !inclusive" do - @property.should = ["foo=baz", "bar=boo"] - @property.expects(:retrieve).returns({:foo => "diff", :do => "re", :mi => "fa"}) - @property.expects(:inclusive?).returns(false) - @property.should.must == { :foo => "baz", :bar => "boo", :do => "re", :mi => "fa" } - end - end - - describe "when calling retrieve" do - before do - @provider = mock("provider") - @property.stubs(:provider).returns(@provider) - end - - it "should send 'name' to the provider" do - @provider.expects(:send).with(:keys) - @property.expects(:name).returns(:keys) - @property.retrieve - end - - it "should return a hash with the provider returned info" do - @provider.stubs(:send).with(:keys).returns({"do" => "re", "mi" => "fa" }) - @property.stubs(:name).returns(:keys) - @property.retrieve == {"do" => "re", "mi" => "fa" } - end - - it "should return :absent when the provider returns :absent" do - @provider.stubs(:send).with(:keys).returns(:absent) - @property.stubs(:name).returns(:keys) - @property.retrieve == :absent - end - end - - describe "when calling hashify" do - it "should return the array hashified" do - @property.hashify(["foo=baz", "bar=boo"]).must == { :foo => "baz", :bar => "boo" } - end - end - - describe "when calling insync?" do - before do - @provider = mock("provider") - @property.stubs(:provider).returns(@provider) - @property.stubs(:name).returns(:prop_name) - end - - it "should return true unless @should is defined and not nil" do - @property.insync?("foo") == true - end - - it "should return true if the passed in values is nil" do - @property.should = "foo" - @property.insync?(nil) == true - end - - it "should return true if hashified should value == (retrieved) value passed in" do - @provider.stubs(:prop_name).returns({ :foo => "baz", :bar => "boo" }) - @property.should = ["foo=baz", "bar=boo"] - @property.expects(:inclusive?).returns(true) - @property.insync?({ :foo => "baz", :bar => "boo" }).must == true - end - - it "should return false if prepared value != should value" do - @provider.stubs(:prop_name).returns({ "foo" => "bee", "bar" => "boo" }) - @property.should = ["foo=baz", "bar=boo"] - @property.expects(:inclusive?).returns(true) - @property.insync?({ "foo" => "bee", "bar" => "boo" }).must == false - end - end - end -end diff --git a/spec/unit/property/list_spec.rb b/spec/unit/property/list_spec.rb new file mode 100644 index 000000000..84d14478b --- /dev/null +++ b/spec/unit/property/list_spec.rb @@ -0,0 +1,166 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/property/list' + +list_class = Puppet::Property::List + +describe list_class do + + it "should be a subclass of Property" do + list_class.superclass.must == Puppet::Property + end + + describe "as an instance" do + before do + # Wow that's a messy interface to the resource. + list_class.initvars + @resource = stub 'resource', :[]= => nil, :property => nil + @property = list_class.new(:resource => @resource) + end + + it "should have a , as default delimiter" do + @property.delimiter.should == "," + end + + it "should have a :membership as default membership" do + @property.membership.should == :membership + end + + it "should return the same value passed into should_to_s" do + @property.should_to_s("foo") == "foo" + end + + it "should return the passed in array values joined with the delimiter from is_to_s" do + @property.is_to_s(["foo","bar"]).should == "foo,bar" + end + + it "should be able to correctly convert ':absent' to a string" do + @property.is_to_s(:absent).should == "absent" + end + + describe "when adding should to current" do + it "should add the arrays when current is an array" do + @property.add_should_with_current(["foo"], ["bar"]).should == ["foo", "bar"] + end + + it "should return should if current is not a array" do + @property.add_should_with_current(["foo"], :absent).should == ["foo"] + end + + it "should return only the uniq elements" do + @property.add_should_with_current(["foo", "bar"], ["foo", "baz"]).should == ["foo", "bar", "baz"] + end + end + + describe "when calling inclusive?" do + it "should use the membership method to look up on the @resource" do + @property.expects(:membership).returns(:membership) + @resource.expects(:[]).with(:membership) + @property.inclusive? + end + + it "should return true when @resource[membership] == inclusive" do + @property.stubs(:membership).returns(:membership) + @resource.stubs(:[]).with(:membership).returns(:inclusive) + @property.inclusive?.must == true + end + + it "should return false when @resource[membership] != inclusive" do + @property.stubs(:membership).returns(:membership) + @resource.stubs(:[]).with(:membership).returns(:minimum) + @property.inclusive?.must == false + end + end + + describe "when calling should" do + it "should return nil if @should is nil" do + @property.should.must == nil + end + + it "should return the sorted values of @should as a string if inclusive" do + @property.should = ["foo", "bar"] + @property.expects(:inclusive?).returns(true) + @property.should.must == "bar,foo" + end + + it "should return the uniq sorted values of @should + retrieve as a string if !inclusive" do + @property.should = ["foo", "bar"] + @property.expects(:inclusive?).returns(false) + @property.expects(:retrieve).returns(["foo","baz"]) + @property.should.must == "bar,baz,foo" + end + end + + describe "when calling retrieve" do + before do + @provider = mock("provider") + @property.stubs(:provider).returns(@provider) + end + + it "should send 'name' to the provider" do + @provider.expects(:send).with(:group) + @property.expects(:name).returns(:group) + @property.retrieve + end + + it "should return an array with the provider returned info" do + @provider.stubs(:send).with(:group).returns("foo,bar,baz") + @property.stubs(:name).returns(:group) + @property.retrieve == ["foo", "bar", "baz"] + end + + it "should return :absent when the provider returns :absent" do + @provider.stubs(:send).with(:group).returns(:absent) + @property.stubs(:name).returns(:group) + @property.retrieve == :absent + end + end + + describe "when calling insync?" do + it "should return true unless @should is defined and not nil" do + @property.must be_insync("foo") + end + + it "should return true unless the passed in values is not nil" do + @property.should = "foo" + @property.must be_insync(nil) + end + + it "should call prepare_is_for_comparison with value passed in and should" do + @property.should = "foo" + @property.expects(:prepare_is_for_comparison).with("bar") + @property.expects(:should) + @property.insync?("bar") + end + + it "should return true if 'is' value is array of comma delimited should values" do + @property.should = "bar,foo" + @property.expects(:inclusive?).returns(true) + @property.must be_insync(["bar","foo"]) + end + + it "should return true if 'is' value is :absent and should value is empty string" do + @property.should = "" + @property.expects(:inclusive?).returns(true) + @property.must be_insync([]) + end + + it "should return false if prepared value != should value" do + @property.should = "bar,baz,foo" + @property.expects(:inclusive?).returns(true) + @property.must_not be_insync(["bar","foo"]) + end + end + + describe "when calling dearrayify" do + it "should sort and join the array with 'delimiter'" do + array = mock "array" + array.expects(:sort).returns(array) + array.expects(:join).with(@property.delimiter) + @property.dearrayify(array) + end + end + end +end diff --git a/spec/unit/property/list_spec_spec.rb b/spec/unit/property/list_spec_spec.rb deleted file mode 100644 index 84d14478b..000000000 --- a/spec/unit/property/list_spec_spec.rb +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/property/list' - -list_class = Puppet::Property::List - -describe list_class do - - it "should be a subclass of Property" do - list_class.superclass.must == Puppet::Property - end - - describe "as an instance" do - before do - # Wow that's a messy interface to the resource. - list_class.initvars - @resource = stub 'resource', :[]= => nil, :property => nil - @property = list_class.new(:resource => @resource) - end - - it "should have a , as default delimiter" do - @property.delimiter.should == "," - end - - it "should have a :membership as default membership" do - @property.membership.should == :membership - end - - it "should return the same value passed into should_to_s" do - @property.should_to_s("foo") == "foo" - end - - it "should return the passed in array values joined with the delimiter from is_to_s" do - @property.is_to_s(["foo","bar"]).should == "foo,bar" - end - - it "should be able to correctly convert ':absent' to a string" do - @property.is_to_s(:absent).should == "absent" - end - - describe "when adding should to current" do - it "should add the arrays when current is an array" do - @property.add_should_with_current(["foo"], ["bar"]).should == ["foo", "bar"] - end - - it "should return should if current is not a array" do - @property.add_should_with_current(["foo"], :absent).should == ["foo"] - end - - it "should return only the uniq elements" do - @property.add_should_with_current(["foo", "bar"], ["foo", "baz"]).should == ["foo", "bar", "baz"] - end - end - - describe "when calling inclusive?" do - it "should use the membership method to look up on the @resource" do - @property.expects(:membership).returns(:membership) - @resource.expects(:[]).with(:membership) - @property.inclusive? - end - - it "should return true when @resource[membership] == inclusive" do - @property.stubs(:membership).returns(:membership) - @resource.stubs(:[]).with(:membership).returns(:inclusive) - @property.inclusive?.must == true - end - - it "should return false when @resource[membership] != inclusive" do - @property.stubs(:membership).returns(:membership) - @resource.stubs(:[]).with(:membership).returns(:minimum) - @property.inclusive?.must == false - end - end - - describe "when calling should" do - it "should return nil if @should is nil" do - @property.should.must == nil - end - - it "should return the sorted values of @should as a string if inclusive" do - @property.should = ["foo", "bar"] - @property.expects(:inclusive?).returns(true) - @property.should.must == "bar,foo" - end - - it "should return the uniq sorted values of @should + retrieve as a string if !inclusive" do - @property.should = ["foo", "bar"] - @property.expects(:inclusive?).returns(false) - @property.expects(:retrieve).returns(["foo","baz"]) - @property.should.must == "bar,baz,foo" - end - end - - describe "when calling retrieve" do - before do - @provider = mock("provider") - @property.stubs(:provider).returns(@provider) - end - - it "should send 'name' to the provider" do - @provider.expects(:send).with(:group) - @property.expects(:name).returns(:group) - @property.retrieve - end - - it "should return an array with the provider returned info" do - @provider.stubs(:send).with(:group).returns("foo,bar,baz") - @property.stubs(:name).returns(:group) - @property.retrieve == ["foo", "bar", "baz"] - end - - it "should return :absent when the provider returns :absent" do - @provider.stubs(:send).with(:group).returns(:absent) - @property.stubs(:name).returns(:group) - @property.retrieve == :absent - end - end - - describe "when calling insync?" do - it "should return true unless @should is defined and not nil" do - @property.must be_insync("foo") - end - - it "should return true unless the passed in values is not nil" do - @property.should = "foo" - @property.must be_insync(nil) - end - - it "should call prepare_is_for_comparison with value passed in and should" do - @property.should = "foo" - @property.expects(:prepare_is_for_comparison).with("bar") - @property.expects(:should) - @property.insync?("bar") - end - - it "should return true if 'is' value is array of comma delimited should values" do - @property.should = "bar,foo" - @property.expects(:inclusive?).returns(true) - @property.must be_insync(["bar","foo"]) - end - - it "should return true if 'is' value is :absent and should value is empty string" do - @property.should = "" - @property.expects(:inclusive?).returns(true) - @property.must be_insync([]) - end - - it "should return false if prepared value != should value" do - @property.should = "bar,baz,foo" - @property.expects(:inclusive?).returns(true) - @property.must_not be_insync(["bar","foo"]) - end - end - - describe "when calling dearrayify" do - it "should sort and join the array with 'delimiter'" do - array = mock "array" - array.expects(:sort).returns(array) - array.expects(:join).with(@property.delimiter) - @property.dearrayify(array) - end - end - end -end diff --git a/spec/unit/property/ordered_list_spec.rb b/spec/unit/property/ordered_list_spec.rb new file mode 100644 index 000000000..51c59a7dd --- /dev/null +++ b/spec/unit/property/ordered_list_spec.rb @@ -0,0 +1,64 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/property/ordered_list' + +ordered_list_class = Puppet::Property::OrderedList + +describe ordered_list_class do + + it "should be a subclass of List" do + ordered_list_class.superclass.must == Puppet::Property::List + end + + describe "as an instance" do + before do + # Wow that's a messy interface to the resource. + ordered_list_class.initvars + @resource = stub 'resource', :[]= => nil, :property => nil + @property = ordered_list_class.new(:resource => @resource) + end + + describe "when adding should to current" do + it "should add the arrays when current is an array" do + @property.add_should_with_current(["should"], ["current"]).should == ["should", "current"] + end + + it "should return 'should' if current is not a array" do + @property.add_should_with_current(["should"], :absent).should == ["should"] + end + + it "should return only the uniq elements leading with the order of 'should'" do + @property.add_should_with_current(["this", "is", "should"], ["is", "this", "current"]).should == ["this", "is", "should", "current"] + end + end + + describe "when calling should" do + it "should return nil if @should is nil" do + @property.should.must == nil + end + + it "should return the values of @should (without sorting) as a string if inclusive" do + @property.should = ["foo", "bar"] + @property.expects(:inclusive?).returns(true) + @property.should.must == "foo,bar" + end + + it "should return the uniq values of @should + retrieve as a string if !inclusive with the @ values leading" do + @property.should = ["foo", "bar"] + @property.expects(:inclusive?).returns(false) + @property.expects(:retrieve).returns(["foo","baz"]) + @property.should.must == "foo,bar,baz" + end + end + + describe "when calling dearrayify" do + it "should join the array with the delimiter" do + array = mock "array" + array.expects(:join).with(@property.delimiter) + @property.dearrayify(array) + end + end + end +end diff --git a/spec/unit/property/ordered_list_spec_spec.rb b/spec/unit/property/ordered_list_spec_spec.rb deleted file mode 100644 index 51c59a7dd..000000000 --- a/spec/unit/property/ordered_list_spec_spec.rb +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/property/ordered_list' - -ordered_list_class = Puppet::Property::OrderedList - -describe ordered_list_class do - - it "should be a subclass of List" do - ordered_list_class.superclass.must == Puppet::Property::List - end - - describe "as an instance" do - before do - # Wow that's a messy interface to the resource. - ordered_list_class.initvars - @resource = stub 'resource', :[]= => nil, :property => nil - @property = ordered_list_class.new(:resource => @resource) - end - - describe "when adding should to current" do - it "should add the arrays when current is an array" do - @property.add_should_with_current(["should"], ["current"]).should == ["should", "current"] - end - - it "should return 'should' if current is not a array" do - @property.add_should_with_current(["should"], :absent).should == ["should"] - end - - it "should return only the uniq elements leading with the order of 'should'" do - @property.add_should_with_current(["this", "is", "should"], ["is", "this", "current"]).should == ["this", "is", "should", "current"] - end - end - - describe "when calling should" do - it "should return nil if @should is nil" do - @property.should.must == nil - end - - it "should return the values of @should (without sorting) as a string if inclusive" do - @property.should = ["foo", "bar"] - @property.expects(:inclusive?).returns(true) - @property.should.must == "foo,bar" - end - - it "should return the uniq values of @should + retrieve as a string if !inclusive with the @ values leading" do - @property.should = ["foo", "bar"] - @property.expects(:inclusive?).returns(false) - @property.expects(:retrieve).returns(["foo","baz"]) - @property.should.must == "foo,bar,baz" - end - end - - describe "when calling dearrayify" do - it "should join the array with the delimiter" do - array = mock "array" - array.expects(:join).with(@property.delimiter) - @property.dearrayify(array) - end - end - end -end diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb new file mode 100755 index 000000000..5c47bcf55 --- /dev/null +++ b/spec/unit/property_spec.rb @@ -0,0 +1,410 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' +require 'puppet/property' + +describe Puppet::Property do + before do + @class = Class.new(Puppet::Property) do + @name = :foo + end + @class.initvars + @provider = mock 'provider' + @resource = stub 'resource', :provider => @provider + @resource.stub_everything + @property = @class.new :resource => @resource + end + + it "should return its name as a string when converted to a string" do + @property.to_s.should == @property.name.to_s + end + + it "should be able to look up the modified name for a given value" do + @class.newvalue(:foo) + @class.value_name("foo").should == :foo + end + + it "should be able to look up the modified name for a given value matching a regex" do + @class.newvalue(%r{.}) + @class.value_name("foo").should == %r{.} + end + + it "should be able to look up a given value option" do + @class.newvalue(:foo, :event => :whatever) + @class.value_option(:foo, :event).should == :whatever + end + + it "should be able to specify required features" do + @class.should respond_to(:required_features=) + end + + {"one" => [:one],:one => [:one],%w{a} => [:a],[:b] => [:b],%w{one two} => [:one,:two],[:a,:b] => [:a,:b]}.each { |in_value,out_value| + it "should always convert required features into an array of symbols (e.g. #{in_value.inspect} --> #{out_value.inspect})" do + @class.required_features = in_value + @class.required_features.should == out_value + end + } + + it "should be able to shadow metaparameters" do + @property.must respond_to(:shadow) + end + + describe "when returning the default event name" do + before do + @resource = stub 'resource' + @instance = @class.new(:resource => @resource) + @instance.stubs(:should).returns "myval" + end + + it "should use the current 'should' value to pick the event name" do + @instance.expects(:should).returns "myvalue" + @class.expects(:value_option).with('myvalue', :event).returns :event_name + + @instance.event_name + end + + it "should return any event defined with the specified value" do + @instance.expects(:should).returns :myval + @class.expects(:value_option).with(:myval, :event).returns :event_name + + @instance.event_name.should == :event_name + end + + describe "and the property is 'ensure'" do + before do + @instance.stubs(:name).returns :ensure + @resource.expects(:type).returns :mytype + end + + it "should use _created if the 'should' value is 'present'" do + @instance.expects(:should).returns :present + @instance.event_name.should == :mytype_created + end + + it "should use _removed if the 'should' value is 'absent'" do + @instance.expects(:should).returns :absent + @instance.event_name.should == :mytype_removed + end + + it "should use _changed if the 'should' value is not 'absent' or 'present'" do + @instance.expects(:should).returns :foo + @instance.event_name.should == :mytype_changed + end + + it "should use _changed if the 'should value is nil" do + @instance.expects(:should).returns nil + @instance.event_name.should == :mytype_changed + end + end + + it "should use _changed if the property is not 'ensure'" do + @instance.stubs(:name).returns :myparam + @instance.expects(:should).returns :foo + @instance.event_name.should == :myparam_changed + end + + it "should use _changed if no 'should' value is set" do + @instance.stubs(:name).returns :myparam + @instance.expects(:should).returns nil + @instance.event_name.should == :myparam_changed + end + end + + describe "when creating an event" do + before do + @event = Puppet::Transaction::Event.new + + # Use a real resource so we can test the event creation integration + @resource = Puppet::Type.type(:mount).new :name => "foo" + @instance = @class.new(:resource => @resource) + @instance.stubs(:should).returns "myval" + end + + it "should use an event from the resource as the base event" do + event = Puppet::Transaction::Event.new + @resource.expects(:event).returns event + + @instance.event.should equal(event) + end + + it "should have the default event name" do + @instance.expects(:event_name).returns :my_event + @instance.event.name.should == :my_event + end + + it "should have the property's name" do + @instance.event.property.should == @instance.name.to_s + end + + it "should have the 'should' value set" do + @instance.stubs(:should).returns "foo" + @instance.event.desired_value.should == "foo" + end + + it "should provide its path as the source description" do + @instance.stubs(:path).returns "/my/param" + @instance.event.source_description.should == "/my/param" + end + end + + describe "when shadowing metaparameters" do + before do + @shadow_class = Class.new(Puppet::Property) do + @name = :alias + end + @shadow_class.initvars + end + + it "should create an instance of the metaparameter at initialization" do + Puppet::Type.metaparamclass(:alias).expects(:new).with(:resource => @resource) + + @shadow_class.new :resource => @resource + end + + it "should munge values using the shadow's munge method" do + shadow = mock 'shadow' + Puppet::Type.metaparamclass(:alias).expects(:new).returns shadow + + shadow.expects(:munge).with "foo" + + property = @shadow_class.new :resource => @resource + property.munge("foo") + end + end + + describe "when defining new values" do + it "should define a method for each value created with a block that's not a regex" do + @class.newvalue(:foo) { } + @property.must respond_to(:set_foo) + end + end + + describe "when assigning the value" do + it "should just set the 'should' value" do + @property.value = "foo" + @property.should.must == "foo" + end + + it "should validate each value separately" do + @property.expects(:validate).with("one") + @property.expects(:validate).with("two") + + @property.value = %w{one two} + end + + it "should munge each value separately and use any result as the actual value" do + @property.expects(:munge).with("one").returns :one + @property.expects(:munge).with("two").returns :two + + # Do this so we get the whole array back. + @class.array_matching = :all + + @property.value = %w{one two} + @property.should.must == [:one, :two] + end + + it "should return any set value" do + (@property.value = :one).should == :one + end + end + + describe "when returning the value" do + it "should return nil if no value is set" do + @property.should.must be_nil + end + + it "should return the first set 'should' value if :array_matching is set to :first" do + @class.array_matching = :first + @property.should = %w{one two} + @property.should.must == "one" + end + + it "should return all set 'should' values as an array if :array_matching is set to :all" do + @class.array_matching = :all + @property.should = %w{one two} + @property.should.must == %w{one two} + end + + it "should default to :first array_matching" do + @class.array_matching.should == :first + end + + it "should unmunge the returned value if :array_matching is set to :first" do + @property.class.unmunge do |v| v.to_sym end + @class.array_matching = :first + @property.should = %w{one two} + + @property.should.must == :one + end + + it "should unmunge all the returned values if :array_matching is set to :all" do + @property.class.unmunge do |v| v.to_sym end + @class.array_matching = :all + @property.should = %w{one two} + + @property.should.must == [:one, :two] + end + end + + describe "when validating values" do + it "should do nothing if no values or regexes have been defined" do + lambda { @property.should = "foo" }.should_not raise_error + end + + it "should fail if the value is not a defined value or alias and does not match a regex" do + @class.newvalue(:foo) + + lambda { @property.should = "bar" }.should raise_error + end + + it "should succeeed if the value is one of the defined values" do + @class.newvalue(:foo) + + lambda { @property.should = :foo }.should_not raise_error + end + + it "should succeeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do + @class.newvalue(:foo) + + lambda { @property.should = "foo" }.should_not raise_error + end + + it "should succeeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do + @class.newvalue("foo") + + lambda { @property.should = :foo }.should_not raise_error + end + + it "should succeed if the value is one of the defined aliases" do + @class.newvalue("foo") + @class.aliasvalue("bar", "foo") + + lambda { @property.should = :bar }.should_not raise_error + end + + it "should succeed if the value matches one of the regexes" do + @class.newvalue(/./) + + lambda { @property.should = "bar" }.should_not raise_error + end + + it "should validate that all required features are present" do + @class.newvalue(:foo, :required_features => [:a, :b]) + + @provider.expects(:satisfies?).with([:a, :b]).returns true + + @property.should = :foo + end + + it "should fail if required features are missing" do + @class.newvalue(:foo, :required_features => [:a, :b]) + + @provider.expects(:satisfies?).with([:a, :b]).returns false + + lambda { @property.should = :foo }.should raise_error(Puppet::Error) + end + + it "should internally raise an ArgumentError if required features are missing" do + @class.newvalue(:foo, :required_features => [:a, :b]) + + @provider.expects(:satisfies?).with([:a, :b]).returns false + + lambda { @property.validate_features_per_value :foo }.should raise_error(ArgumentError) + end + + it "should validate that all required features are present for regexes" do + value = @class.newvalue(/./, :required_features => [:a, :b]) + + @provider.expects(:satisfies?).with([:a, :b]).returns true + + @property.should = "foo" + end + + it "should support specifying an individual required feature" do + value = @class.newvalue(/./, :required_features => :a) + + @provider.expects(:satisfies?).returns true + + @property.should = "foo" + end + end + + describe "when munging values" do + it "should do nothing if no values or regexes have been defined" do + @property.munge("foo").should == "foo" + end + + it "should return return any matching defined values" do + @class.newvalue(:foo) + @property.munge("foo").should == :foo + end + + it "should return any matching aliases" do + @class.newvalue(:foo) + @class.aliasvalue(:bar, :foo) + @property.munge("bar").should == :foo + end + + it "should return the value if it matches a regex" do + @class.newvalue(/./) + @property.munge("bar").should == "bar" + end + + it "should return the value if no other option is matched" do + @class.newvalue(:foo) + @property.munge("bar").should == "bar" + end + end + + describe "when syncing the 'should' value" do + it "should set the value" do + @class.newvalue(:foo) + @property.should = :foo + @property.expects(:set).with(:foo) + @property.sync + end + end + + describe "when setting a value" do + it "should catch exceptions and raise Puppet::Error" do + @class.newvalue(:foo) { raise "eh" } + lambda { @property.set(:foo) }.should raise_error(Puppet::Error) + end + + describe "that was defined without a block" do + it "should call the settor on the provider" do + @class.newvalue(:bar) + @provider.expects(:foo=).with :bar + @property.set(:bar) + end + end + + describe "that was defined with a block" do + it "should call the method created for the value if the value is not a regex" do + @class.newvalue(:bar) {} + @property.expects(:set_bar) + @property.set(:bar) + end + + it "should call the provided block if the value is a regex" do + @class.newvalue(/./) { self.test } + @property.expects(:test) + @property.set("foo") + end + end + end + + describe "when producing a change log" do + it "should say 'defined' when the current value is 'absent'" do + @property.change_to_s(:absent, "foo").should =~ /^defined/ + end + + it "should say 'undefined' when the new value is 'absent'" do + @property.change_to_s("foo", :absent).should =~ /^undefined/ + end + + it "should say 'changed' when neither value is 'absent'" do + @property.change_to_s("foo", "bar").should =~ /changed/ + end + end +end diff --git a/spec/unit/property_spec_spec.rb b/spec/unit/property_spec_spec.rb deleted file mode 100755 index 5c47bcf55..000000000 --- a/spec/unit/property_spec_spec.rb +++ /dev/null @@ -1,410 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' -require 'puppet/property' - -describe Puppet::Property do - before do - @class = Class.new(Puppet::Property) do - @name = :foo - end - @class.initvars - @provider = mock 'provider' - @resource = stub 'resource', :provider => @provider - @resource.stub_everything - @property = @class.new :resource => @resource - end - - it "should return its name as a string when converted to a string" do - @property.to_s.should == @property.name.to_s - end - - it "should be able to look up the modified name for a given value" do - @class.newvalue(:foo) - @class.value_name("foo").should == :foo - end - - it "should be able to look up the modified name for a given value matching a regex" do - @class.newvalue(%r{.}) - @class.value_name("foo").should == %r{.} - end - - it "should be able to look up a given value option" do - @class.newvalue(:foo, :event => :whatever) - @class.value_option(:foo, :event).should == :whatever - end - - it "should be able to specify required features" do - @class.should respond_to(:required_features=) - end - - {"one" => [:one],:one => [:one],%w{a} => [:a],[:b] => [:b],%w{one two} => [:one,:two],[:a,:b] => [:a,:b]}.each { |in_value,out_value| - it "should always convert required features into an array of symbols (e.g. #{in_value.inspect} --> #{out_value.inspect})" do - @class.required_features = in_value - @class.required_features.should == out_value - end - } - - it "should be able to shadow metaparameters" do - @property.must respond_to(:shadow) - end - - describe "when returning the default event name" do - before do - @resource = stub 'resource' - @instance = @class.new(:resource => @resource) - @instance.stubs(:should).returns "myval" - end - - it "should use the current 'should' value to pick the event name" do - @instance.expects(:should).returns "myvalue" - @class.expects(:value_option).with('myvalue', :event).returns :event_name - - @instance.event_name - end - - it "should return any event defined with the specified value" do - @instance.expects(:should).returns :myval - @class.expects(:value_option).with(:myval, :event).returns :event_name - - @instance.event_name.should == :event_name - end - - describe "and the property is 'ensure'" do - before do - @instance.stubs(:name).returns :ensure - @resource.expects(:type).returns :mytype - end - - it "should use _created if the 'should' value is 'present'" do - @instance.expects(:should).returns :present - @instance.event_name.should == :mytype_created - end - - it "should use _removed if the 'should' value is 'absent'" do - @instance.expects(:should).returns :absent - @instance.event_name.should == :mytype_removed - end - - it "should use _changed if the 'should' value is not 'absent' or 'present'" do - @instance.expects(:should).returns :foo - @instance.event_name.should == :mytype_changed - end - - it "should use _changed if the 'should value is nil" do - @instance.expects(:should).returns nil - @instance.event_name.should == :mytype_changed - end - end - - it "should use _changed if the property is not 'ensure'" do - @instance.stubs(:name).returns :myparam - @instance.expects(:should).returns :foo - @instance.event_name.should == :myparam_changed - end - - it "should use _changed if no 'should' value is set" do - @instance.stubs(:name).returns :myparam - @instance.expects(:should).returns nil - @instance.event_name.should == :myparam_changed - end - end - - describe "when creating an event" do - before do - @event = Puppet::Transaction::Event.new - - # Use a real resource so we can test the event creation integration - @resource = Puppet::Type.type(:mount).new :name => "foo" - @instance = @class.new(:resource => @resource) - @instance.stubs(:should).returns "myval" - end - - it "should use an event from the resource as the base event" do - event = Puppet::Transaction::Event.new - @resource.expects(:event).returns event - - @instance.event.should equal(event) - end - - it "should have the default event name" do - @instance.expects(:event_name).returns :my_event - @instance.event.name.should == :my_event - end - - it "should have the property's name" do - @instance.event.property.should == @instance.name.to_s - end - - it "should have the 'should' value set" do - @instance.stubs(:should).returns "foo" - @instance.event.desired_value.should == "foo" - end - - it "should provide its path as the source description" do - @instance.stubs(:path).returns "/my/param" - @instance.event.source_description.should == "/my/param" - end - end - - describe "when shadowing metaparameters" do - before do - @shadow_class = Class.new(Puppet::Property) do - @name = :alias - end - @shadow_class.initvars - end - - it "should create an instance of the metaparameter at initialization" do - Puppet::Type.metaparamclass(:alias).expects(:new).with(:resource => @resource) - - @shadow_class.new :resource => @resource - end - - it "should munge values using the shadow's munge method" do - shadow = mock 'shadow' - Puppet::Type.metaparamclass(:alias).expects(:new).returns shadow - - shadow.expects(:munge).with "foo" - - property = @shadow_class.new :resource => @resource - property.munge("foo") - end - end - - describe "when defining new values" do - it "should define a method for each value created with a block that's not a regex" do - @class.newvalue(:foo) { } - @property.must respond_to(:set_foo) - end - end - - describe "when assigning the value" do - it "should just set the 'should' value" do - @property.value = "foo" - @property.should.must == "foo" - end - - it "should validate each value separately" do - @property.expects(:validate).with("one") - @property.expects(:validate).with("two") - - @property.value = %w{one two} - end - - it "should munge each value separately and use any result as the actual value" do - @property.expects(:munge).with("one").returns :one - @property.expects(:munge).with("two").returns :two - - # Do this so we get the whole array back. - @class.array_matching = :all - - @property.value = %w{one two} - @property.should.must == [:one, :two] - end - - it "should return any set value" do - (@property.value = :one).should == :one - end - end - - describe "when returning the value" do - it "should return nil if no value is set" do - @property.should.must be_nil - end - - it "should return the first set 'should' value if :array_matching is set to :first" do - @class.array_matching = :first - @property.should = %w{one two} - @property.should.must == "one" - end - - it "should return all set 'should' values as an array if :array_matching is set to :all" do - @class.array_matching = :all - @property.should = %w{one two} - @property.should.must == %w{one two} - end - - it "should default to :first array_matching" do - @class.array_matching.should == :first - end - - it "should unmunge the returned value if :array_matching is set to :first" do - @property.class.unmunge do |v| v.to_sym end - @class.array_matching = :first - @property.should = %w{one two} - - @property.should.must == :one - end - - it "should unmunge all the returned values if :array_matching is set to :all" do - @property.class.unmunge do |v| v.to_sym end - @class.array_matching = :all - @property.should = %w{one two} - - @property.should.must == [:one, :two] - end - end - - describe "when validating values" do - it "should do nothing if no values or regexes have been defined" do - lambda { @property.should = "foo" }.should_not raise_error - end - - it "should fail if the value is not a defined value or alias and does not match a regex" do - @class.newvalue(:foo) - - lambda { @property.should = "bar" }.should raise_error - end - - it "should succeeed if the value is one of the defined values" do - @class.newvalue(:foo) - - lambda { @property.should = :foo }.should_not raise_error - end - - it "should succeeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do - @class.newvalue(:foo) - - lambda { @property.should = "foo" }.should_not raise_error - end - - it "should succeeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do - @class.newvalue("foo") - - lambda { @property.should = :foo }.should_not raise_error - end - - it "should succeed if the value is one of the defined aliases" do - @class.newvalue("foo") - @class.aliasvalue("bar", "foo") - - lambda { @property.should = :bar }.should_not raise_error - end - - it "should succeed if the value matches one of the regexes" do - @class.newvalue(/./) - - lambda { @property.should = "bar" }.should_not raise_error - end - - it "should validate that all required features are present" do - @class.newvalue(:foo, :required_features => [:a, :b]) - - @provider.expects(:satisfies?).with([:a, :b]).returns true - - @property.should = :foo - end - - it "should fail if required features are missing" do - @class.newvalue(:foo, :required_features => [:a, :b]) - - @provider.expects(:satisfies?).with([:a, :b]).returns false - - lambda { @property.should = :foo }.should raise_error(Puppet::Error) - end - - it "should internally raise an ArgumentError if required features are missing" do - @class.newvalue(:foo, :required_features => [:a, :b]) - - @provider.expects(:satisfies?).with([:a, :b]).returns false - - lambda { @property.validate_features_per_value :foo }.should raise_error(ArgumentError) - end - - it "should validate that all required features are present for regexes" do - value = @class.newvalue(/./, :required_features => [:a, :b]) - - @provider.expects(:satisfies?).with([:a, :b]).returns true - - @property.should = "foo" - end - - it "should support specifying an individual required feature" do - value = @class.newvalue(/./, :required_features => :a) - - @provider.expects(:satisfies?).returns true - - @property.should = "foo" - end - end - - describe "when munging values" do - it "should do nothing if no values or regexes have been defined" do - @property.munge("foo").should == "foo" - end - - it "should return return any matching defined values" do - @class.newvalue(:foo) - @property.munge("foo").should == :foo - end - - it "should return any matching aliases" do - @class.newvalue(:foo) - @class.aliasvalue(:bar, :foo) - @property.munge("bar").should == :foo - end - - it "should return the value if it matches a regex" do - @class.newvalue(/./) - @property.munge("bar").should == "bar" - end - - it "should return the value if no other option is matched" do - @class.newvalue(:foo) - @property.munge("bar").should == "bar" - end - end - - describe "when syncing the 'should' value" do - it "should set the value" do - @class.newvalue(:foo) - @property.should = :foo - @property.expects(:set).with(:foo) - @property.sync - end - end - - describe "when setting a value" do - it "should catch exceptions and raise Puppet::Error" do - @class.newvalue(:foo) { raise "eh" } - lambda { @property.set(:foo) }.should raise_error(Puppet::Error) - end - - describe "that was defined without a block" do - it "should call the settor on the provider" do - @class.newvalue(:bar) - @provider.expects(:foo=).with :bar - @property.set(:bar) - end - end - - describe "that was defined with a block" do - it "should call the method created for the value if the value is not a regex" do - @class.newvalue(:bar) {} - @property.expects(:set_bar) - @property.set(:bar) - end - - it "should call the provided block if the value is a regex" do - @class.newvalue(/./) { self.test } - @property.expects(:test) - @property.set("foo") - end - end - end - - describe "when producing a change log" do - it "should say 'defined' when the current value is 'absent'" do - @property.change_to_s(:absent, "foo").should =~ /^defined/ - end - - it "should say 'undefined' when the new value is 'absent'" do - @property.change_to_s("foo", :absent).should =~ /^undefined/ - end - - it "should say 'changed' when neither value is 'absent'" do - @property.change_to_s("foo", "bar").should =~ /changed/ - end - end -end diff --git a/spec/unit/provider/augeas/augeas_spec.rb b/spec/unit/provider/augeas/augeas_spec.rb new file mode 100644 index 000000000..22f7bed8c --- /dev/null +++ b/spec/unit/provider/augeas/augeas_spec.rb @@ -0,0 +1,413 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:augeas).provider(:augeas) + +describe provider_class do + + describe "command parsing" do + before do + @resource = stub("resource") + @provider = provider_class.new(@resource) + end + + it "should break apart a single line into three tokens and clean up the context" do + @resource.stubs(:[]).returns("/context") + tokens = @provider.parse_commands("set Jar/Jar Binks") + tokens.size.should == 1 + tokens[0].size.should == 3 + tokens[0][0].should == "set" + tokens[0][1].should == "/context/Jar/Jar" + tokens[0][2].should == "Binks" + end + + it "should break apart a multiple line into six tokens" do + @resource.stubs(:[]).returns("") + tokens = @provider.parse_commands("set /Jar/Jar Binks\nrm anakin") + tokens.size.should == 2 + tokens[0].size.should == 3 + tokens[1].size.should == 2 + tokens[0][0].should == "set" + tokens[0][1].should == "/Jar/Jar" + tokens[0][2].should == "Binks" + tokens[1][0].should == "rm" + tokens[1][1].should == "anakin" + end + + it "should strip whitespace and ignore blank lines" do + @resource.stubs(:[]).returns("") + tokens = @provider.parse_commands(" set /Jar/Jar Binks \t\n \n\n rm anakin ") + tokens.size.should == 2 + tokens[0].size.should == 3 + tokens[1].size.should == 2 + tokens[0][0].should == "set" + tokens[0][1].should == "/Jar/Jar" + tokens[0][2].should == "Binks" + tokens[1][0].should == "rm" + tokens[1][1].should == "anakin" + end + + it "should handle arrays" do + @resource.stubs(:[]).returns("/foo/") + commands = ["set /Jar/Jar Binks", "rm anakin"] + tokens = @provider.parse_commands(commands) + tokens.size.should == 2 + tokens[0].size.should == 3 + tokens[1].size.should == 2 + tokens[0][0].should == "set" + tokens[0][1].should == "/Jar/Jar" + tokens[0][2].should == "Binks" + tokens[1][0].should == "rm" + tokens[1][1].should == "/foo/anakin" + end + + # This is not supported in the new parsing class + #it "should concat the last values" do + # provider = provider_class.new() + # tokens = provider.parse_commands("set /Jar/Jar Binks is my copilot") + # tokens.size.should == 1 + # tokens[0].size.should == 3 + # tokens[0][0].should == "set" + # tokens[0][1].should == "/Jar/Jar" + # tokens[0][2].should == "Binks is my copilot" + #end + + it "should accept spaces in the value and single ticks" do + @resource.stubs(:[]).returns("/foo/") + tokens = @provider.parse_commands("set JarJar 'Binks is my copilot'") + tokens.size.should == 1 + tokens[0].size.should == 3 + tokens[0][0].should == "set" + tokens[0][1].should == "/foo/JarJar" + tokens[0][2].should == "Binks is my copilot" + end + + it "should accept spaces in the value and double ticks" do + @resource.stubs(:[]).returns("/foo/") + tokens = @provider.parse_commands('set /JarJar "Binks is my copilot"') + tokens.size.should == 1 + tokens[0].size.should == 3 + tokens[0][0].should == "set" + tokens[0][1].should == '/JarJar' + tokens[0][2].should == 'Binks is my copilot' + end + + it "should accept mixed ticks" do + @resource.stubs(:[]).returns("/foo/") + tokens = @provider.parse_commands('set JarJar "Some \'Test\'"') + tokens.size.should == 1 + tokens[0].size.should == 3 + tokens[0][0].should == "set" + tokens[0][1].should == '/foo/JarJar' + tokens[0][2].should == "Some \'Test\'" + end + + it "should handle predicates with literals" do + @resource.stubs(:[]).returns("/foo/") + tokens = @provider.parse_commands("rm */*[module='pam_console.so']") + tokens.should == [["rm", "/foo/*/*[module='pam_console.so']"]] + end + + it "should handle whitespace in predicates" do + @resource.stubs(:[]).returns("/foo/") + tokens = @provider.parse_commands("ins 42 before /files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]") + tokens.should == [["ins", "42", "before","/files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]"]] + end + + it "should handle multiple predicates" do + @resource.stubs(:[]).returns("/foo/") + tokens = @provider.parse_commands("clear pam.d/*/*[module = 'system-auth'][type = 'account']") + tokens.should == [["clear", "/foo/pam.d/*/*[module = 'system-auth'][type = 'account']"]] + end + + it "should handle nested predicates" do + @resource.stubs(:[]).returns("/foo/") + args = ["clear", "/foo/pam.d/*/*[module[ ../type = 'type] = 'system-auth'][type[last()] = 'account']"] + tokens = @provider.parse_commands(args.join(" ")) + tokens.should == [ args ] + end + + it "should handle escaped doublequotes in doublequoted string" do + @resource.stubs(:[]).returns("/foo/") + tokens = @provider.parse_commands("set /foo \"''\\\"''\"") + tokens.should == [[ "set", "/foo", "''\\\"''" ]] + end + + it "should allow escaped spaces and brackets in paths" do + @resource.stubs(:[]).returns("/foo/") + args = [ "set", "/white\\ space/\\[section", "value" ] + tokens = @provider.parse_commands(args.join(" \t ")) + tokens.should == [ args ] + end + + it "should allow single quoted escaped spaces in paths" do + @resource.stubs(:[]).returns("/foo/") + args = [ "set", "'/white\\ space/key'", "value" ] + tokens = @provider.parse_commands(args.join(" \t ")) + tokens.should == [[ "set", "/white\\ space/key", "value" ]] + end + + it "should allow double quoted escaped spaces in paths" do + @resource.stubs(:[]).returns("/foo/") + args = [ "set", '"/white\\ space/key"', "value" ] + tokens = @provider.parse_commands(args.join(" \t ")) + tokens.should == [[ "set", "/white\\ space/key", "value" ]] + end + + it "should remove trailing slashes" do + @resource.stubs(:[]).returns("/foo/") + tokens = @provider.parse_commands("set foo/ bar") + tokens.should == [[ "set", "/foo/foo", "bar" ]] + end + end + + describe "get filters" do + before do + augeas_stub = stub("augeas", :get => "value") + @provider = provider_class.new() + @provider.aug= augeas_stub + end + + it "should return false for a = nonmatch" do + command = ["get", "fake value", "==", "value"] + @provider.process_get(command).should == true + end + + it "should return true for a != match" do + command = ["get", "fake value", "!=", "value"] + @provider.process_get(command).should == false + end + + it "should return true for a =~ match" do + command = ["get", "fake value", "=~", "val*"] + @provider.process_get(command).should == true + end + + it "should return false for a == nonmatch" do + command = ["get", "fake value", "=~", "num*"] + @provider.process_get(command).should == false + end + end + + describe "match filters" do + before do + resource = stub("resource", :[] => "") + augeas_stub = stub("augeas", :match => ["set", "of", "values"]) + @provider = provider_class.new(resource) + @provider.aug= augeas_stub + end + + it "should return true for size match" do + command = ["match", "fake value", "size == 3"] + @provider.process_match(command).should == true + end + + it "should return false for a size non match" do + command = ["match", "fake value", "size < 3"] + @provider.process_match(command).should == false + end + + it "should return true for includes match" do + command = ["match", "fake value", "include values"] + @provider.process_match(command).should == true + end + + it "should return false for includes non match" do + command = ["match", "fake value", "include JarJar"] + @provider.process_match(command).should == false + end + + it "should return true for includes match" do + command = ["match", "fake value", "not_include JarJar"] + @provider.process_match(command).should == true + end + + it "should return false for includes non match" do + command = ["match", "fake value", "not_include values"] + @provider.process_match(command).should == false + end + + it "should return true for an array match" do + command = ["match", "fake value", "== ['set', 'of', 'values']"] + @provider.process_match(command).should == true + end + + it "should return false for an array non match" do + command = ["match", "fake value", "== ['this', 'should', 'not', 'match']"] + @provider.process_match(command).should == false + end + + it "should return false for an array match with noteq" do + command = ["match", "fake value", "!= ['set', 'of', 'values']"] + @provider.process_match(command).should == false + end + + it "should return true for an array non match with noteq" do + command = ["match", "fake value", "!= ['this', 'should', 'not', 'match']"] + @provider.process_match(command).should == true + end + end + + describe "need to run" do + it "should handle no filters" do + resource = stub("resource") + resource.stubs(:[]).returns(false).then.returns("").then.returns("") + augeas_stub = stub("augeas", :match => ["set", "of", "values"]) + augeas_stub.stubs("close") + provider = provider_class.new(resource) + provider.aug= augeas_stub + provider.stubs(:get_augeas_version).returns("0.3.5") + provider.need_to_run?.should == true + end + + it "should return true when a get filter matches" do + resource = stub("resource") + resource.stubs(:[]).returns(false).then.returns("get path == value").then.returns("") + provider = provider_class.new(resource) + augeas_stub = stub("augeas", :get => "value") + augeas_stub.stubs("close") + provider.aug= augeas_stub + provider.stubs(:get_augeas_version).returns("0.3.5") + provider.need_to_run?.should == true + end + + it "should return false when a get filter does not match" do + resource = stub("resource") + resource.stubs(:[]).returns(false).then.returns("get path == another value").then.returns("") + provider = provider_class.new(resource) + augeas_stub = stub("augeas", :get => "value") + augeas_stub.stubs("close") + provider.aug= augeas_stub + provider.stubs(:get_augeas_version).returns("0.3.5") + provider.need_to_run?.should == false + end + + it "should return true when a match filter matches" do + resource = stub("resource") + resource.stubs(:[]).returns(false).then.returns("match path size == 3").then.returns("") + provider = provider_class.new(resource) + augeas_stub = stub("augeas", :match => ["set", "of", "values"]) + augeas_stub.stubs("close") + provider.aug= augeas_stub + provider.stubs(:get_augeas_version).returns("0.3.5") + provider.need_to_run?.should == true + end + + it "should return false when a match filter does not match" do + resource = stub("resource") + resource.stubs(:[]).returns(false).then.returns("match path size == 2").then.returns("") + provider = provider_class.new(resource) + augeas_stub = stub("augeas", :match => ["set", "of", "values"]) + augeas_stub.stubs("close") + provider.aug= augeas_stub + provider.stubs(:get_augeas_version).returns("0.3.5") + provider.need_to_run?.should == false + end + + #This is a copy of the last one, with setting the force to true + it "setting force should not change the above logic" do + resource = stub("resource") + resource.stubs(:[]).returns(true).then.returns("match path size == 2").then.returns("") + provider = provider_class.new(resource) + augeas_stub = stub("augeas", :match => ["set", "of", "values"]) + augeas_stub.stubs("close") + provider.aug= augeas_stub + provider.stubs(:get_augeas_version).returns("0.3.5") + provider.need_to_run?.should == false + end + end + + describe "augeas execution integration" do + + before do + @resource = stub("resource") + @provider = provider_class.new(@resource) + @augeas = stub("augeas") + @provider.aug= @augeas + @provider.stubs(:get_augeas_version).returns("0.3.5") + end + + it "should handle set commands" do + command = "set JarJar Binks" + context = "/some/path/" + @resource.expects(:[]).times(2).returns(command).then.returns(context) + @augeas.expects(:set).with("/some/path/JarJar", "Binks").returns(true) + @augeas.expects(:save).returns(true) + @augeas.expects(:close) + @provider.execute_changes.should == :executed + end + + it "should handle rm commands" do + command = "rm /Jar/Jar" + context = "" + @resource.expects(:[]).times(2).returns(command).then.returns(context) + @augeas.expects(:rm).with("/Jar/Jar") + @augeas.expects(:save).returns(true) + @augeas.expects(:close) + @provider.execute_changes.should == :executed + end + + it "should handle remove commands" do + command = "remove /Jar/Jar" + context = "" + @resource.expects(:[]).times(2).returns(command).then.returns(context) + @augeas.expects(:rm).with("/Jar/Jar") + @augeas.expects(:save).returns(true) + @augeas.expects(:close) + @provider.execute_changes.should == :executed + end + + it "should handle clear commands" do + command = "clear Jar/Jar" + context = "/foo/" + @resource.expects(:[]).times(2).returns(command).then.returns(context) + @augeas.expects(:clear).with("/foo/Jar/Jar").returns(true) + @augeas.expects(:save).returns(true) + @augeas.expects(:close) + @provider.execute_changes.should == :executed + end + + + it "should handle ins commands with before" do + command = "ins Binks before Jar/Jar" + context = "/foo" + @resource.expects(:[]).times(2).returns(command).then.returns(context) + @augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", true) + @augeas.expects(:save).returns(true) + @augeas.expects(:close) + @provider.execute_changes.should == :executed + end + + it "should handle ins commands with after" do + command = "ins Binks after /Jar/Jar" + context = "/foo" + @resource.expects(:[]).times(2).returns(command).then.returns(context) + @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) + @augeas.expects(:save).returns(true) + @augeas.expects(:close) + @provider.execute_changes.should == :executed + end + + it "should handle ins with no context" do + command = "ins Binks after /Jar/Jar" + context = "" # this is the default + @resource.expects(:[]).times(2).returns(command).then.returns(context) + @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) + @augeas.expects(:save).returns(true) + @augeas.expects(:close) + @provider.execute_changes.should == :executed + end + + it "should handle multiple commands" do + command = ["ins Binks after /Jar/Jar", "clear Jar/Jar"] + context = "/foo/" + @resource.expects(:[]).times(2).returns(command).then.returns(context) + @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) + @augeas.expects(:clear).with("/foo/Jar/Jar").returns(true) + @augeas.expects(:save).returns(true) + @augeas.expects(:close) + @provider.execute_changes.should == :executed + end + end +end diff --git a/spec/unit/provider/augeas/augeas_spec_spec.rb b/spec/unit/provider/augeas/augeas_spec_spec.rb deleted file mode 100644 index 22f7bed8c..000000000 --- a/spec/unit/provider/augeas/augeas_spec_spec.rb +++ /dev/null @@ -1,413 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:augeas).provider(:augeas) - -describe provider_class do - - describe "command parsing" do - before do - @resource = stub("resource") - @provider = provider_class.new(@resource) - end - - it "should break apart a single line into three tokens and clean up the context" do - @resource.stubs(:[]).returns("/context") - tokens = @provider.parse_commands("set Jar/Jar Binks") - tokens.size.should == 1 - tokens[0].size.should == 3 - tokens[0][0].should == "set" - tokens[0][1].should == "/context/Jar/Jar" - tokens[0][2].should == "Binks" - end - - it "should break apart a multiple line into six tokens" do - @resource.stubs(:[]).returns("") - tokens = @provider.parse_commands("set /Jar/Jar Binks\nrm anakin") - tokens.size.should == 2 - tokens[0].size.should == 3 - tokens[1].size.should == 2 - tokens[0][0].should == "set" - tokens[0][1].should == "/Jar/Jar" - tokens[0][2].should == "Binks" - tokens[1][0].should == "rm" - tokens[1][1].should == "anakin" - end - - it "should strip whitespace and ignore blank lines" do - @resource.stubs(:[]).returns("") - tokens = @provider.parse_commands(" set /Jar/Jar Binks \t\n \n\n rm anakin ") - tokens.size.should == 2 - tokens[0].size.should == 3 - tokens[1].size.should == 2 - tokens[0][0].should == "set" - tokens[0][1].should == "/Jar/Jar" - tokens[0][2].should == "Binks" - tokens[1][0].should == "rm" - tokens[1][1].should == "anakin" - end - - it "should handle arrays" do - @resource.stubs(:[]).returns("/foo/") - commands = ["set /Jar/Jar Binks", "rm anakin"] - tokens = @provider.parse_commands(commands) - tokens.size.should == 2 - tokens[0].size.should == 3 - tokens[1].size.should == 2 - tokens[0][0].should == "set" - tokens[0][1].should == "/Jar/Jar" - tokens[0][2].should == "Binks" - tokens[1][0].should == "rm" - tokens[1][1].should == "/foo/anakin" - end - - # This is not supported in the new parsing class - #it "should concat the last values" do - # provider = provider_class.new() - # tokens = provider.parse_commands("set /Jar/Jar Binks is my copilot") - # tokens.size.should == 1 - # tokens[0].size.should == 3 - # tokens[0][0].should == "set" - # tokens[0][1].should == "/Jar/Jar" - # tokens[0][2].should == "Binks is my copilot" - #end - - it "should accept spaces in the value and single ticks" do - @resource.stubs(:[]).returns("/foo/") - tokens = @provider.parse_commands("set JarJar 'Binks is my copilot'") - tokens.size.should == 1 - tokens[0].size.should == 3 - tokens[0][0].should == "set" - tokens[0][1].should == "/foo/JarJar" - tokens[0][2].should == "Binks is my copilot" - end - - it "should accept spaces in the value and double ticks" do - @resource.stubs(:[]).returns("/foo/") - tokens = @provider.parse_commands('set /JarJar "Binks is my copilot"') - tokens.size.should == 1 - tokens[0].size.should == 3 - tokens[0][0].should == "set" - tokens[0][1].should == '/JarJar' - tokens[0][2].should == 'Binks is my copilot' - end - - it "should accept mixed ticks" do - @resource.stubs(:[]).returns("/foo/") - tokens = @provider.parse_commands('set JarJar "Some \'Test\'"') - tokens.size.should == 1 - tokens[0].size.should == 3 - tokens[0][0].should == "set" - tokens[0][1].should == '/foo/JarJar' - tokens[0][2].should == "Some \'Test\'" - end - - it "should handle predicates with literals" do - @resource.stubs(:[]).returns("/foo/") - tokens = @provider.parse_commands("rm */*[module='pam_console.so']") - tokens.should == [["rm", "/foo/*/*[module='pam_console.so']"]] - end - - it "should handle whitespace in predicates" do - @resource.stubs(:[]).returns("/foo/") - tokens = @provider.parse_commands("ins 42 before /files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]") - tokens.should == [["ins", "42", "before","/files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]"]] - end - - it "should handle multiple predicates" do - @resource.stubs(:[]).returns("/foo/") - tokens = @provider.parse_commands("clear pam.d/*/*[module = 'system-auth'][type = 'account']") - tokens.should == [["clear", "/foo/pam.d/*/*[module = 'system-auth'][type = 'account']"]] - end - - it "should handle nested predicates" do - @resource.stubs(:[]).returns("/foo/") - args = ["clear", "/foo/pam.d/*/*[module[ ../type = 'type] = 'system-auth'][type[last()] = 'account']"] - tokens = @provider.parse_commands(args.join(" ")) - tokens.should == [ args ] - end - - it "should handle escaped doublequotes in doublequoted string" do - @resource.stubs(:[]).returns("/foo/") - tokens = @provider.parse_commands("set /foo \"''\\\"''\"") - tokens.should == [[ "set", "/foo", "''\\\"''" ]] - end - - it "should allow escaped spaces and brackets in paths" do - @resource.stubs(:[]).returns("/foo/") - args = [ "set", "/white\\ space/\\[section", "value" ] - tokens = @provider.parse_commands(args.join(" \t ")) - tokens.should == [ args ] - end - - it "should allow single quoted escaped spaces in paths" do - @resource.stubs(:[]).returns("/foo/") - args = [ "set", "'/white\\ space/key'", "value" ] - tokens = @provider.parse_commands(args.join(" \t ")) - tokens.should == [[ "set", "/white\\ space/key", "value" ]] - end - - it "should allow double quoted escaped spaces in paths" do - @resource.stubs(:[]).returns("/foo/") - args = [ "set", '"/white\\ space/key"', "value" ] - tokens = @provider.parse_commands(args.join(" \t ")) - tokens.should == [[ "set", "/white\\ space/key", "value" ]] - end - - it "should remove trailing slashes" do - @resource.stubs(:[]).returns("/foo/") - tokens = @provider.parse_commands("set foo/ bar") - tokens.should == [[ "set", "/foo/foo", "bar" ]] - end - end - - describe "get filters" do - before do - augeas_stub = stub("augeas", :get => "value") - @provider = provider_class.new() - @provider.aug= augeas_stub - end - - it "should return false for a = nonmatch" do - command = ["get", "fake value", "==", "value"] - @provider.process_get(command).should == true - end - - it "should return true for a != match" do - command = ["get", "fake value", "!=", "value"] - @provider.process_get(command).should == false - end - - it "should return true for a =~ match" do - command = ["get", "fake value", "=~", "val*"] - @provider.process_get(command).should == true - end - - it "should return false for a == nonmatch" do - command = ["get", "fake value", "=~", "num*"] - @provider.process_get(command).should == false - end - end - - describe "match filters" do - before do - resource = stub("resource", :[] => "") - augeas_stub = stub("augeas", :match => ["set", "of", "values"]) - @provider = provider_class.new(resource) - @provider.aug= augeas_stub - end - - it "should return true for size match" do - command = ["match", "fake value", "size == 3"] - @provider.process_match(command).should == true - end - - it "should return false for a size non match" do - command = ["match", "fake value", "size < 3"] - @provider.process_match(command).should == false - end - - it "should return true for includes match" do - command = ["match", "fake value", "include values"] - @provider.process_match(command).should == true - end - - it "should return false for includes non match" do - command = ["match", "fake value", "include JarJar"] - @provider.process_match(command).should == false - end - - it "should return true for includes match" do - command = ["match", "fake value", "not_include JarJar"] - @provider.process_match(command).should == true - end - - it "should return false for includes non match" do - command = ["match", "fake value", "not_include values"] - @provider.process_match(command).should == false - end - - it "should return true for an array match" do - command = ["match", "fake value", "== ['set', 'of', 'values']"] - @provider.process_match(command).should == true - end - - it "should return false for an array non match" do - command = ["match", "fake value", "== ['this', 'should', 'not', 'match']"] - @provider.process_match(command).should == false - end - - it "should return false for an array match with noteq" do - command = ["match", "fake value", "!= ['set', 'of', 'values']"] - @provider.process_match(command).should == false - end - - it "should return true for an array non match with noteq" do - command = ["match", "fake value", "!= ['this', 'should', 'not', 'match']"] - @provider.process_match(command).should == true - end - end - - describe "need to run" do - it "should handle no filters" do - resource = stub("resource") - resource.stubs(:[]).returns(false).then.returns("").then.returns("") - augeas_stub = stub("augeas", :match => ["set", "of", "values"]) - augeas_stub.stubs("close") - provider = provider_class.new(resource) - provider.aug= augeas_stub - provider.stubs(:get_augeas_version).returns("0.3.5") - provider.need_to_run?.should == true - end - - it "should return true when a get filter matches" do - resource = stub("resource") - resource.stubs(:[]).returns(false).then.returns("get path == value").then.returns("") - provider = provider_class.new(resource) - augeas_stub = stub("augeas", :get => "value") - augeas_stub.stubs("close") - provider.aug= augeas_stub - provider.stubs(:get_augeas_version).returns("0.3.5") - provider.need_to_run?.should == true - end - - it "should return false when a get filter does not match" do - resource = stub("resource") - resource.stubs(:[]).returns(false).then.returns("get path == another value").then.returns("") - provider = provider_class.new(resource) - augeas_stub = stub("augeas", :get => "value") - augeas_stub.stubs("close") - provider.aug= augeas_stub - provider.stubs(:get_augeas_version).returns("0.3.5") - provider.need_to_run?.should == false - end - - it "should return true when a match filter matches" do - resource = stub("resource") - resource.stubs(:[]).returns(false).then.returns("match path size == 3").then.returns("") - provider = provider_class.new(resource) - augeas_stub = stub("augeas", :match => ["set", "of", "values"]) - augeas_stub.stubs("close") - provider.aug= augeas_stub - provider.stubs(:get_augeas_version).returns("0.3.5") - provider.need_to_run?.should == true - end - - it "should return false when a match filter does not match" do - resource = stub("resource") - resource.stubs(:[]).returns(false).then.returns("match path size == 2").then.returns("") - provider = provider_class.new(resource) - augeas_stub = stub("augeas", :match => ["set", "of", "values"]) - augeas_stub.stubs("close") - provider.aug= augeas_stub - provider.stubs(:get_augeas_version).returns("0.3.5") - provider.need_to_run?.should == false - end - - #This is a copy of the last one, with setting the force to true - it "setting force should not change the above logic" do - resource = stub("resource") - resource.stubs(:[]).returns(true).then.returns("match path size == 2").then.returns("") - provider = provider_class.new(resource) - augeas_stub = stub("augeas", :match => ["set", "of", "values"]) - augeas_stub.stubs("close") - provider.aug= augeas_stub - provider.stubs(:get_augeas_version).returns("0.3.5") - provider.need_to_run?.should == false - end - end - - describe "augeas execution integration" do - - before do - @resource = stub("resource") - @provider = provider_class.new(@resource) - @augeas = stub("augeas") - @provider.aug= @augeas - @provider.stubs(:get_augeas_version).returns("0.3.5") - end - - it "should handle set commands" do - command = "set JarJar Binks" - context = "/some/path/" - @resource.expects(:[]).times(2).returns(command).then.returns(context) - @augeas.expects(:set).with("/some/path/JarJar", "Binks").returns(true) - @augeas.expects(:save).returns(true) - @augeas.expects(:close) - @provider.execute_changes.should == :executed - end - - it "should handle rm commands" do - command = "rm /Jar/Jar" - context = "" - @resource.expects(:[]).times(2).returns(command).then.returns(context) - @augeas.expects(:rm).with("/Jar/Jar") - @augeas.expects(:save).returns(true) - @augeas.expects(:close) - @provider.execute_changes.should == :executed - end - - it "should handle remove commands" do - command = "remove /Jar/Jar" - context = "" - @resource.expects(:[]).times(2).returns(command).then.returns(context) - @augeas.expects(:rm).with("/Jar/Jar") - @augeas.expects(:save).returns(true) - @augeas.expects(:close) - @provider.execute_changes.should == :executed - end - - it "should handle clear commands" do - command = "clear Jar/Jar" - context = "/foo/" - @resource.expects(:[]).times(2).returns(command).then.returns(context) - @augeas.expects(:clear).with("/foo/Jar/Jar").returns(true) - @augeas.expects(:save).returns(true) - @augeas.expects(:close) - @provider.execute_changes.should == :executed - end - - - it "should handle ins commands with before" do - command = "ins Binks before Jar/Jar" - context = "/foo" - @resource.expects(:[]).times(2).returns(command).then.returns(context) - @augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", true) - @augeas.expects(:save).returns(true) - @augeas.expects(:close) - @provider.execute_changes.should == :executed - end - - it "should handle ins commands with after" do - command = "ins Binks after /Jar/Jar" - context = "/foo" - @resource.expects(:[]).times(2).returns(command).then.returns(context) - @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) - @augeas.expects(:save).returns(true) - @augeas.expects(:close) - @provider.execute_changes.should == :executed - end - - it "should handle ins with no context" do - command = "ins Binks after /Jar/Jar" - context = "" # this is the default - @resource.expects(:[]).times(2).returns(command).then.returns(context) - @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) - @augeas.expects(:save).returns(true) - @augeas.expects(:close) - @provider.execute_changes.should == :executed - end - - it "should handle multiple commands" do - command = ["ins Binks after /Jar/Jar", "clear Jar/Jar"] - context = "/foo/" - @resource.expects(:[]).times(2).returns(command).then.returns(context) - @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) - @augeas.expects(:clear).with("/foo/Jar/Jar").returns(true) - @augeas.expects(:save).returns(true) - @augeas.expects(:close) - @provider.execute_changes.should == :executed - end - end -end diff --git a/spec/unit/provider/confine/exists_spec.rb b/spec/unit/provider/confine/exists_spec.rb new file mode 100755 index 000000000..a3539c4f5 --- /dev/null +++ b/spec/unit/provider/confine/exists_spec.rb @@ -0,0 +1,81 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/confine/exists' + +describe Puppet::Provider::Confine::Exists do + before do + @confine = Puppet::Provider::Confine::Exists.new("/my/file") + @confine.label = "eh" + end + + it "should be named :exists" do + Puppet::Provider::Confine::Exists.name.should == :exists + end + + it "should use the 'pass?' method to test validity" do + @confine.expects(:pass?).with("/my/file") + @confine.valid? + end + + it "should return false if the value is false" do + @confine.pass?(false).should be_false + end + + it "should return false if the value does not point to a file" do + FileTest.expects(:exist?).with("/my/file").returns false + @confine.pass?("/my/file").should be_false + end + + it "should return true if the value points to a file" do + FileTest.expects(:exist?).with("/my/file").returns true + @confine.pass?("/my/file").should be_true + end + + it "should produce a message saying that a file is missing" do + @confine.message("/my/file").should be_include("does not exist") + end + + describe "and the confine is for binaries" do + before { @confine.stubs(:for_binary).returns true } + it "should use its 'binary' method to look up the full path of the file" do + @confine.expects(:binary).returns nil + @confine.pass?("/my/file") + end + + it "should return false if no binary can be found" do + @confine.expects(:binary).with("/my/file").returns nil + @confine.pass?("/my/file").should be_false + end + + it "should return true if the binary can be found and the file exists" do + @confine.expects(:binary).with("/my/file").returns "/my/file" + FileTest.expects(:exist?).with("/my/file").returns true + @confine.pass?("/my/file").should be_true + end + + it "should return false if the binary can be found but the file does not exist" do + @confine.expects(:binary).with("/my/file").returns "/my/file" + FileTest.expects(:exist?).with("/my/file").returns true + @confine.pass?("/my/file").should be_true + end + end + + it "should produce a summary containing all missing files" do + FileTest.stubs(:exist?).returns true + FileTest.expects(:exist?).with("/two").returns false + FileTest.expects(:exist?).with("/four").returns false + + confine = Puppet::Provider::Confine::Exists.new %w{/one /two /three /four} + confine.summary.should == %w{/two /four} + end + + it "should summarize multiple instances by returning a flattened array of their summaries" do + c1 = mock '1', :summary => %w{one} + c2 = mock '2', :summary => %w{two} + c3 = mock '3', :summary => %w{three} + + Puppet::Provider::Confine::Exists.summarize([c1, c2, c3]).should == %w{one two three} + end +end diff --git a/spec/unit/provider/confine/exists_spec_spec.rb b/spec/unit/provider/confine/exists_spec_spec.rb deleted file mode 100755 index a3539c4f5..000000000 --- a/spec/unit/provider/confine/exists_spec_spec.rb +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/provider/confine/exists' - -describe Puppet::Provider::Confine::Exists do - before do - @confine = Puppet::Provider::Confine::Exists.new("/my/file") - @confine.label = "eh" - end - - it "should be named :exists" do - Puppet::Provider::Confine::Exists.name.should == :exists - end - - it "should use the 'pass?' method to test validity" do - @confine.expects(:pass?).with("/my/file") - @confine.valid? - end - - it "should return false if the value is false" do - @confine.pass?(false).should be_false - end - - it "should return false if the value does not point to a file" do - FileTest.expects(:exist?).with("/my/file").returns false - @confine.pass?("/my/file").should be_false - end - - it "should return true if the value points to a file" do - FileTest.expects(:exist?).with("/my/file").returns true - @confine.pass?("/my/file").should be_true - end - - it "should produce a message saying that a file is missing" do - @confine.message("/my/file").should be_include("does not exist") - end - - describe "and the confine is for binaries" do - before { @confine.stubs(:for_binary).returns true } - it "should use its 'binary' method to look up the full path of the file" do - @confine.expects(:binary).returns nil - @confine.pass?("/my/file") - end - - it "should return false if no binary can be found" do - @confine.expects(:binary).with("/my/file").returns nil - @confine.pass?("/my/file").should be_false - end - - it "should return true if the binary can be found and the file exists" do - @confine.expects(:binary).with("/my/file").returns "/my/file" - FileTest.expects(:exist?).with("/my/file").returns true - @confine.pass?("/my/file").should be_true - end - - it "should return false if the binary can be found but the file does not exist" do - @confine.expects(:binary).with("/my/file").returns "/my/file" - FileTest.expects(:exist?).with("/my/file").returns true - @confine.pass?("/my/file").should be_true - end - end - - it "should produce a summary containing all missing files" do - FileTest.stubs(:exist?).returns true - FileTest.expects(:exist?).with("/two").returns false - FileTest.expects(:exist?).with("/four").returns false - - confine = Puppet::Provider::Confine::Exists.new %w{/one /two /three /four} - confine.summary.should == %w{/two /four} - end - - it "should summarize multiple instances by returning a flattened array of their summaries" do - c1 = mock '1', :summary => %w{one} - c2 = mock '2', :summary => %w{two} - c3 = mock '3', :summary => %w{three} - - Puppet::Provider::Confine::Exists.summarize([c1, c2, c3]).should == %w{one two three} - end -end diff --git a/spec/unit/provider/confine/false_spec.rb b/spec/unit/provider/confine/false_spec.rb new file mode 100755 index 000000000..6ff5cc133 --- /dev/null +++ b/spec/unit/provider/confine/false_spec.rb @@ -0,0 +1,53 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/confine/false' + +describe Puppet::Provider::Confine::False do + it "should be named :false" do + Puppet::Provider::Confine::False.name.should == :false + end + + it "should require a value" do + lambda { Puppet::Provider::Confine.new() }.should raise_error(ArgumentError) + end + + describe "when testing values" do + before { @confine = Puppet::Provider::Confine::False.new("foo") } + + it "should use the 'pass?' method to test validity" do + @confine = Puppet::Provider::Confine::False.new("foo") + @confine.label = "eh" + @confine.expects(:pass?).with("foo") + @confine.valid? + end + + it "should return true if the value is false" do + @confine.pass?(false).should be_true + end + + it "should return false if the value is not false" do + @confine.pass?("else").should be_false + end + + it "should produce a message that a value is true" do + @confine = Puppet::Provider::Confine::False.new("foo") + @confine.message("eh").should be_include("true") + end + end + + it "should be able to produce a summary with the number of incorrectly true values" do + confine = Puppet::Provider::Confine::False.new %w{one two three four} + confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false) + confine.summary.should == 2 + end + + it "should summarize multiple instances by summing their summaries" do + c1 = mock '1', :summary => 1 + c2 = mock '2', :summary => 2 + c3 = mock '3', :summary => 3 + + Puppet::Provider::Confine::False.summarize([c1, c2, c3]).should == 6 + end +end diff --git a/spec/unit/provider/confine/false_spec_spec.rb b/spec/unit/provider/confine/false_spec_spec.rb deleted file mode 100755 index 6ff5cc133..000000000 --- a/spec/unit/provider/confine/false_spec_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/provider/confine/false' - -describe Puppet::Provider::Confine::False do - it "should be named :false" do - Puppet::Provider::Confine::False.name.should == :false - end - - it "should require a value" do - lambda { Puppet::Provider::Confine.new() }.should raise_error(ArgumentError) - end - - describe "when testing values" do - before { @confine = Puppet::Provider::Confine::False.new("foo") } - - it "should use the 'pass?' method to test validity" do - @confine = Puppet::Provider::Confine::False.new("foo") - @confine.label = "eh" - @confine.expects(:pass?).with("foo") - @confine.valid? - end - - it "should return true if the value is false" do - @confine.pass?(false).should be_true - end - - it "should return false if the value is not false" do - @confine.pass?("else").should be_false - end - - it "should produce a message that a value is true" do - @confine = Puppet::Provider::Confine::False.new("foo") - @confine.message("eh").should be_include("true") - end - end - - it "should be able to produce a summary with the number of incorrectly true values" do - confine = Puppet::Provider::Confine::False.new %w{one two three four} - confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false) - confine.summary.should == 2 - end - - it "should summarize multiple instances by summing their summaries" do - c1 = mock '1', :summary => 1 - c2 = mock '2', :summary => 2 - c3 = mock '3', :summary => 3 - - Puppet::Provider::Confine::False.summarize([c1, c2, c3]).should == 6 - end -end diff --git a/spec/unit/provider/confine/feature_spec.rb b/spec/unit/provider/confine/feature_spec.rb new file mode 100755 index 000000000..67e59363e --- /dev/null +++ b/spec/unit/provider/confine/feature_spec.rb @@ -0,0 +1,60 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/confine/feature' + +describe Puppet::Provider::Confine::Feature do + it "should be named :feature" do + Puppet::Provider::Confine::Feature.name.should == :feature + end + + it "should require a value" do + lambda { Puppet::Provider::Confine::Feature.new() }.should raise_error(ArgumentError) + end + + it "should always convert values to an array" do + Puppet::Provider::Confine::Feature.new("/some/file").values.should be_instance_of(Array) + end + + describe "when testing values" do + before do + @features = mock 'features' + Puppet.stubs(:features).returns @features + @confine = Puppet::Provider::Confine::Feature.new("myfeature") + @confine.label = "eh" + end + + it "should use the Puppet features instance to test validity" do + @features.expects(:myfeature?) + @confine.valid? + end + + it "should return true if the feature is present" do + @features.expects(:myfeature?).returns true + @confine.pass?("myfeature").should be_true + end + + it "should return false if the value is false" do + @features.expects(:myfeature?).returns false + @confine.pass?("myfeature").should be_false + end + + it "should log that a feature is missing" do + @confine.message("myfeat").should be_include("missing") + end + end + + it "should summarize multiple instances by returning a flattened array of all missing features" do + confines = [] + confines << Puppet::Provider::Confine::Feature.new(%w{one two}) + confines << Puppet::Provider::Confine::Feature.new(%w{two}) + confines << Puppet::Provider::Confine::Feature.new(%w{three four}) + + features = mock 'feature' + features.stub_everything + Puppet.stubs(:features).returns features + + Puppet::Provider::Confine::Feature.summarize(confines).sort.should == %w{one two three four}.sort + end +end diff --git a/spec/unit/provider/confine/feature_spec_spec.rb b/spec/unit/provider/confine/feature_spec_spec.rb deleted file mode 100755 index 67e59363e..000000000 --- a/spec/unit/provider/confine/feature_spec_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/provider/confine/feature' - -describe Puppet::Provider::Confine::Feature do - it "should be named :feature" do - Puppet::Provider::Confine::Feature.name.should == :feature - end - - it "should require a value" do - lambda { Puppet::Provider::Confine::Feature.new() }.should raise_error(ArgumentError) - end - - it "should always convert values to an array" do - Puppet::Provider::Confine::Feature.new("/some/file").values.should be_instance_of(Array) - end - - describe "when testing values" do - before do - @features = mock 'features' - Puppet.stubs(:features).returns @features - @confine = Puppet::Provider::Confine::Feature.new("myfeature") - @confine.label = "eh" - end - - it "should use the Puppet features instance to test validity" do - @features.expects(:myfeature?) - @confine.valid? - end - - it "should return true if the feature is present" do - @features.expects(:myfeature?).returns true - @confine.pass?("myfeature").should be_true - end - - it "should return false if the value is false" do - @features.expects(:myfeature?).returns false - @confine.pass?("myfeature").should be_false - end - - it "should log that a feature is missing" do - @confine.message("myfeat").should be_include("missing") - end - end - - it "should summarize multiple instances by returning a flattened array of all missing features" do - confines = [] - confines << Puppet::Provider::Confine::Feature.new(%w{one two}) - confines << Puppet::Provider::Confine::Feature.new(%w{two}) - confines << Puppet::Provider::Confine::Feature.new(%w{three four}) - - features = mock 'feature' - features.stub_everything - Puppet.stubs(:features).returns features - - Puppet::Provider::Confine::Feature.summarize(confines).sort.should == %w{one two three four}.sort - end -end diff --git a/spec/unit/provider/confine/true_spec.rb b/spec/unit/provider/confine/true_spec.rb new file mode 100755 index 000000000..75b36cea5 --- /dev/null +++ b/spec/unit/provider/confine/true_spec.rb @@ -0,0 +1,53 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/confine/true' + +describe Puppet::Provider::Confine::True do + it "should be named :true" do + Puppet::Provider::Confine::True.name.should == :true + end + + it "should require a value" do + lambda { Puppet::Provider::Confine::True.new() }.should raise_error(ArgumentError) + end + + describe "when testing values" do + before do + @confine = Puppet::Provider::Confine::True.new("foo") + @confine.label = "eh" + end + + it "should use the 'pass?' method to test validity" do + @confine.expects(:pass?).with("foo") + @confine.valid? + end + + it "should return true if the value is not false" do + @confine.pass?("else").should be_true + end + + it "should return false if the value is false" do + @confine.pass?(nil).should be_false + end + + it "should produce the message that a value is false" do + @confine.message("eh").should be_include("false") + end + end + + it "should produce the number of false values when asked for a summary" do + @confine = Puppet::Provider::Confine::True.new %w{one two three four} + @confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false) + @confine.summary.should == 2 + end + + it "should summarize multiple instances by summing their summaries" do + c1 = mock '1', :summary => 1 + c2 = mock '2', :summary => 2 + c3 = mock '3', :summary => 3 + + Puppet::Provider::Confine::True.summarize([c1, c2, c3]).should == 6 + end +end diff --git a/spec/unit/provider/confine/true_spec_spec.rb b/spec/unit/provider/confine/true_spec_spec.rb deleted file mode 100755 index 75b36cea5..000000000 --- a/spec/unit/provider/confine/true_spec_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/provider/confine/true' - -describe Puppet::Provider::Confine::True do - it "should be named :true" do - Puppet::Provider::Confine::True.name.should == :true - end - - it "should require a value" do - lambda { Puppet::Provider::Confine::True.new() }.should raise_error(ArgumentError) - end - - describe "when testing values" do - before do - @confine = Puppet::Provider::Confine::True.new("foo") - @confine.label = "eh" - end - - it "should use the 'pass?' method to test validity" do - @confine.expects(:pass?).with("foo") - @confine.valid? - end - - it "should return true if the value is not false" do - @confine.pass?("else").should be_true - end - - it "should return false if the value is false" do - @confine.pass?(nil).should be_false - end - - it "should produce the message that a value is false" do - @confine.message("eh").should be_include("false") - end - end - - it "should produce the number of false values when asked for a summary" do - @confine = Puppet::Provider::Confine::True.new %w{one two three four} - @confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false) - @confine.summary.should == 2 - end - - it "should summarize multiple instances by summing their summaries" do - c1 = mock '1', :summary => 1 - c2 = mock '2', :summary => 2 - c3 = mock '3', :summary => 3 - - Puppet::Provider::Confine::True.summarize([c1, c2, c3]).should == 6 - end -end diff --git a/spec/unit/provider/confine/variable_spec.rb b/spec/unit/provider/confine/variable_spec.rb new file mode 100755 index 000000000..7a71fc1ae --- /dev/null +++ b/spec/unit/provider/confine/variable_spec.rb @@ -0,0 +1,107 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/confine/variable' + +describe Puppet::Provider::Confine::Variable do + it "should be named :variable" do + Puppet::Provider::Confine::Variable.name.should == :variable + end + + it "should require a value" do + lambda { Puppet::Provider::Confine::Variable.new() }.should raise_error(ArgumentError) + end + + it "should always convert values to an array" do + Puppet::Provider::Confine::Variable.new("/some/file").values.should be_instance_of(Array) + end + + it "should have an accessor for its name" do + Puppet::Provider::Confine::Variable.new(:bar).should respond_to(:name) + end + + describe "when testing values" do + before do + @confine = Puppet::Provider::Confine::Variable.new("foo") + @confine.name = :myvar + end + + it "should use settings if the variable name is a valid setting" do + Puppet.settings.expects(:valid?).with(:myvar).returns true + Puppet.settings.expects(:value).with(:myvar).returns "foo" + @confine.valid? + end + + it "should use Facter if the variable name is not a valid setting" do + Puppet.settings.expects(:valid?).with(:myvar).returns false + Facter.expects(:value).with(:myvar).returns "foo" + @confine.valid? + end + + it "should be valid if the value matches the facter value" do + @confine.expects(:test_value).returns "foo" + + @confine.should be_valid + end + + it "should return false if the value does not match the facter value" do + @confine.expects(:test_value).returns "fee" + + @confine.should_not be_valid + end + + it "should be case insensitive" do + @confine.expects(:test_value).returns "FOO" + + @confine.should be_valid + end + + it "should not care whether the value is a string or symbol" do + @confine.expects(:test_value).returns "FOO" + + @confine.should be_valid + end + + it "should produce a message that the fact value is not correct" do + @confine = Puppet::Provider::Confine::Variable.new(%w{bar bee}) + @confine.name = "eh" + message = @confine.message("value") + message.should be_include("facter") + message.should be_include("bar,bee") + end + + it "should be valid if the test value matches any of the provided values" do + @confine = Puppet::Provider::Confine::Variable.new(%w{bar bee}) + @confine.expects(:test_value).returns "bee" + @confine.should be_valid + end + end + + describe "when summarizing multiple instances" do + it "should return a hash of failing variables and their values" do + c1 = Puppet::Provider::Confine::Variable.new("one") + c1.name = "uno" + c1.expects(:valid?).returns false + c2 = Puppet::Provider::Confine::Variable.new("two") + c2.name = "dos" + c2.expects(:valid?).returns true + c3 = Puppet::Provider::Confine::Variable.new("three") + c3.name = "tres" + c3.expects(:valid?).returns false + + Puppet::Provider::Confine::Variable.summarize([c1, c2, c3]).should == {"uno" => %w{one}, "tres" => %w{three}} + end + + it "should combine the values of multiple confines with the same fact" do + c1 = Puppet::Provider::Confine::Variable.new("one") + c1.name = "uno" + c1.expects(:valid?).returns false + c2 = Puppet::Provider::Confine::Variable.new("two") + c2.name = "uno" + c2.expects(:valid?).returns false + + Puppet::Provider::Confine::Variable.summarize([c1, c2]).should == {"uno" => %w{one two}} + end + end +end diff --git a/spec/unit/provider/confine/variable_spec_spec.rb b/spec/unit/provider/confine/variable_spec_spec.rb deleted file mode 100755 index 7a71fc1ae..000000000 --- a/spec/unit/provider/confine/variable_spec_spec.rb +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/provider/confine/variable' - -describe Puppet::Provider::Confine::Variable do - it "should be named :variable" do - Puppet::Provider::Confine::Variable.name.should == :variable - end - - it "should require a value" do - lambda { Puppet::Provider::Confine::Variable.new() }.should raise_error(ArgumentError) - end - - it "should always convert values to an array" do - Puppet::Provider::Confine::Variable.new("/some/file").values.should be_instance_of(Array) - end - - it "should have an accessor for its name" do - Puppet::Provider::Confine::Variable.new(:bar).should respond_to(:name) - end - - describe "when testing values" do - before do - @confine = Puppet::Provider::Confine::Variable.new("foo") - @confine.name = :myvar - end - - it "should use settings if the variable name is a valid setting" do - Puppet.settings.expects(:valid?).with(:myvar).returns true - Puppet.settings.expects(:value).with(:myvar).returns "foo" - @confine.valid? - end - - it "should use Facter if the variable name is not a valid setting" do - Puppet.settings.expects(:valid?).with(:myvar).returns false - Facter.expects(:value).with(:myvar).returns "foo" - @confine.valid? - end - - it "should be valid if the value matches the facter value" do - @confine.expects(:test_value).returns "foo" - - @confine.should be_valid - end - - it "should return false if the value does not match the facter value" do - @confine.expects(:test_value).returns "fee" - - @confine.should_not be_valid - end - - it "should be case insensitive" do - @confine.expects(:test_value).returns "FOO" - - @confine.should be_valid - end - - it "should not care whether the value is a string or symbol" do - @confine.expects(:test_value).returns "FOO" - - @confine.should be_valid - end - - it "should produce a message that the fact value is not correct" do - @confine = Puppet::Provider::Confine::Variable.new(%w{bar bee}) - @confine.name = "eh" - message = @confine.message("value") - message.should be_include("facter") - message.should be_include("bar,bee") - end - - it "should be valid if the test value matches any of the provided values" do - @confine = Puppet::Provider::Confine::Variable.new(%w{bar bee}) - @confine.expects(:test_value).returns "bee" - @confine.should be_valid - end - end - - describe "when summarizing multiple instances" do - it "should return a hash of failing variables and their values" do - c1 = Puppet::Provider::Confine::Variable.new("one") - c1.name = "uno" - c1.expects(:valid?).returns false - c2 = Puppet::Provider::Confine::Variable.new("two") - c2.name = "dos" - c2.expects(:valid?).returns true - c3 = Puppet::Provider::Confine::Variable.new("three") - c3.name = "tres" - c3.expects(:valid?).returns false - - Puppet::Provider::Confine::Variable.summarize([c1, c2, c3]).should == {"uno" => %w{one}, "tres" => %w{three}} - end - - it "should combine the values of multiple confines with the same fact" do - c1 = Puppet::Provider::Confine::Variable.new("one") - c1.name = "uno" - c1.expects(:valid?).returns false - c2 = Puppet::Provider::Confine::Variable.new("two") - c2.name = "uno" - c2.expects(:valid?).returns false - - Puppet::Provider::Confine::Variable.summarize([c1, c2]).should == {"uno" => %w{one two}} - end - end -end diff --git a/spec/unit/provider/confine_collection_spec.rb b/spec/unit/provider/confine_collection_spec.rb new file mode 100755 index 000000000..444281c77 --- /dev/null +++ b/spec/unit/provider/confine_collection_spec.rb @@ -0,0 +1,134 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/confine_collection' + +describe Puppet::Provider::ConfineCollection do + it "should be able to add confines" do + Puppet::Provider::ConfineCollection.new("label").should respond_to(:confine) + end + + it "should require a label at initialization" do + lambda { Puppet::Provider::ConfineCollection.new }.should raise_error(ArgumentError) + end + + it "should make its label available" do + Puppet::Provider::ConfineCollection.new("mylabel").label.should == "mylabel" + end + + describe "when creating confine instances" do + it "should create an instance of the named test with the provided values" do + test_class = mock 'test_class' + test_class.expects(:new).with(%w{my values}).returns(stub('confine', :label= => nil)) + Puppet::Provider::Confine.expects(:test).with(:foo).returns test_class + + Puppet::Provider::ConfineCollection.new("label").confine :foo => %w{my values} + end + + it "should copy its label to the confine instance" do + confine = mock 'confine' + test_class = mock 'test_class' + test_class.expects(:new).returns confine + Puppet::Provider::Confine.expects(:test).returns test_class + + confine.expects(:label=).with("label") + + Puppet::Provider::ConfineCollection.new("label").confine :foo => %w{my values} + end + + describe "and the test cannot be found" do + it "should create a Facter test with the provided values and set the name to the test name" do + confine = Puppet::Provider::Confine.test(:variable).new(%w{my values}) + confine.expects(:name=).with(:foo) + confine.class.expects(:new).with(%w{my values}).returns confine + Puppet::Provider::ConfineCollection.new("label").confine(:foo => %w{my values}) + end + end + + describe "and the 'for_binary' option was provided" do + it "should mark the test as a binary confine" do + confine = Puppet::Provider::Confine.test(:exists).new(:bar) + confine.expects(:for_binary=).with true + Puppet::Provider::Confine.test(:exists).expects(:new).with(:bar).returns confine + Puppet::Provider::ConfineCollection.new("label").confine :exists => :bar, :for_binary => true + end + end + end + + it "should be valid if no confines are present" do + Puppet::Provider::ConfineCollection.new("label").should be_valid + end + + it "should be valid if all confines pass" do + c1 = stub 'c1', :valid? => true, :label= => nil + c2 = stub 'c2', :valid? => true, :label= => nil + + Puppet::Provider::Confine.test(:true).expects(:new).returns(c1) + Puppet::Provider::Confine.test(:false).expects(:new).returns(c2) + + confiner = Puppet::Provider::ConfineCollection.new("label") + confiner.confine :true => :bar, :false => :bee + + confiner.should be_valid + end + + it "should not be valid if any confines fail" do + c1 = stub 'c1', :valid? => true, :label= => nil + c2 = stub 'c2', :valid? => false, :label= => nil + + Puppet::Provider::Confine.test(:true).expects(:new).returns(c1) + Puppet::Provider::Confine.test(:false).expects(:new).returns(c2) + + confiner = Puppet::Provider::ConfineCollection.new("label") + confiner.confine :true => :bar, :false => :bee + + confiner.should_not be_valid + end + + describe "when providing a summary" do + before do + @confiner = Puppet::Provider::ConfineCollection.new("label") + end + + it "should return a hash" do + @confiner.summary.should be_instance_of(Hash) + end + + it "should return an empty hash if the confiner is valid" do + @confiner.summary.should == {} + end + + it "should add each test type's summary to the hash" do + @confiner.confine :true => :bar, :false => :bee + Puppet::Provider::Confine.test(:true).expects(:summarize).returns :tsumm + Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm + + @confiner.summary.should == {:true => :tsumm, :false => :fsumm} + end + + it "should not include tests that return 0" do + @confiner.confine :true => :bar, :false => :bee + Puppet::Provider::Confine.test(:true).expects(:summarize).returns 0 + Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm + + @confiner.summary.should == {:false => :fsumm} + end + + it "should not include tests that return empty arrays" do + @confiner.confine :true => :bar, :false => :bee + Puppet::Provider::Confine.test(:true).expects(:summarize).returns [] + Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm + + @confiner.summary.should == {:false => :fsumm} + end + + it "should not include tests that return empty hashes" do + @confiner.confine :true => :bar, :false => :bee + Puppet::Provider::Confine.test(:true).expects(:summarize).returns({}) + Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm + + @confiner.summary.should == {:false => :fsumm} + end + end +end diff --git a/spec/unit/provider/confine_collection_spec_spec.rb b/spec/unit/provider/confine_collection_spec_spec.rb deleted file mode 100755 index 444281c77..000000000 --- a/spec/unit/provider/confine_collection_spec_spec.rb +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/provider/confine_collection' - -describe Puppet::Provider::ConfineCollection do - it "should be able to add confines" do - Puppet::Provider::ConfineCollection.new("label").should respond_to(:confine) - end - - it "should require a label at initialization" do - lambda { Puppet::Provider::ConfineCollection.new }.should raise_error(ArgumentError) - end - - it "should make its label available" do - Puppet::Provider::ConfineCollection.new("mylabel").label.should == "mylabel" - end - - describe "when creating confine instances" do - it "should create an instance of the named test with the provided values" do - test_class = mock 'test_class' - test_class.expects(:new).with(%w{my values}).returns(stub('confine', :label= => nil)) - Puppet::Provider::Confine.expects(:test).with(:foo).returns test_class - - Puppet::Provider::ConfineCollection.new("label").confine :foo => %w{my values} - end - - it "should copy its label to the confine instance" do - confine = mock 'confine' - test_class = mock 'test_class' - test_class.expects(:new).returns confine - Puppet::Provider::Confine.expects(:test).returns test_class - - confine.expects(:label=).with("label") - - Puppet::Provider::ConfineCollection.new("label").confine :foo => %w{my values} - end - - describe "and the test cannot be found" do - it "should create a Facter test with the provided values and set the name to the test name" do - confine = Puppet::Provider::Confine.test(:variable).new(%w{my values}) - confine.expects(:name=).with(:foo) - confine.class.expects(:new).with(%w{my values}).returns confine - Puppet::Provider::ConfineCollection.new("label").confine(:foo => %w{my values}) - end - end - - describe "and the 'for_binary' option was provided" do - it "should mark the test as a binary confine" do - confine = Puppet::Provider::Confine.test(:exists).new(:bar) - confine.expects(:for_binary=).with true - Puppet::Provider::Confine.test(:exists).expects(:new).with(:bar).returns confine - Puppet::Provider::ConfineCollection.new("label").confine :exists => :bar, :for_binary => true - end - end - end - - it "should be valid if no confines are present" do - Puppet::Provider::ConfineCollection.new("label").should be_valid - end - - it "should be valid if all confines pass" do - c1 = stub 'c1', :valid? => true, :label= => nil - c2 = stub 'c2', :valid? => true, :label= => nil - - Puppet::Provider::Confine.test(:true).expects(:new).returns(c1) - Puppet::Provider::Confine.test(:false).expects(:new).returns(c2) - - confiner = Puppet::Provider::ConfineCollection.new("label") - confiner.confine :true => :bar, :false => :bee - - confiner.should be_valid - end - - it "should not be valid if any confines fail" do - c1 = stub 'c1', :valid? => true, :label= => nil - c2 = stub 'c2', :valid? => false, :label= => nil - - Puppet::Provider::Confine.test(:true).expects(:new).returns(c1) - Puppet::Provider::Confine.test(:false).expects(:new).returns(c2) - - confiner = Puppet::Provider::ConfineCollection.new("label") - confiner.confine :true => :bar, :false => :bee - - confiner.should_not be_valid - end - - describe "when providing a summary" do - before do - @confiner = Puppet::Provider::ConfineCollection.new("label") - end - - it "should return a hash" do - @confiner.summary.should be_instance_of(Hash) - end - - it "should return an empty hash if the confiner is valid" do - @confiner.summary.should == {} - end - - it "should add each test type's summary to the hash" do - @confiner.confine :true => :bar, :false => :bee - Puppet::Provider::Confine.test(:true).expects(:summarize).returns :tsumm - Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm - - @confiner.summary.should == {:true => :tsumm, :false => :fsumm} - end - - it "should not include tests that return 0" do - @confiner.confine :true => :bar, :false => :bee - Puppet::Provider::Confine.test(:true).expects(:summarize).returns 0 - Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm - - @confiner.summary.should == {:false => :fsumm} - end - - it "should not include tests that return empty arrays" do - @confiner.confine :true => :bar, :false => :bee - Puppet::Provider::Confine.test(:true).expects(:summarize).returns [] - Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm - - @confiner.summary.should == {:false => :fsumm} - end - - it "should not include tests that return empty hashes" do - @confiner.confine :true => :bar, :false => :bee - Puppet::Provider::Confine.test(:true).expects(:summarize).returns({}) - Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm - - @confiner.summary.should == {:false => :fsumm} - end - end -end diff --git a/spec/unit/provider/confine_spec.rb b/spec/unit/provider/confine_spec.rb new file mode 100755 index 000000000..626f79b27 --- /dev/null +++ b/spec/unit/provider/confine_spec.rb @@ -0,0 +1,78 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/confine' + +describe Puppet::Provider::Confine do + it "should require a value" do + lambda { Puppet::Provider::Confine.new() }.should raise_error(ArgumentError) + end + + it "should always convert values to an array" do + Puppet::Provider::Confine.new("/some/file").values.should be_instance_of(Array) + end + + it "should have a 'true' test" do + Puppet::Provider::Confine.test(:true).should be_instance_of(Class) + end + + it "should have a 'false' test" do + Puppet::Provider::Confine.test(:false).should be_instance_of(Class) + end + + it "should have a 'feature' test" do + Puppet::Provider::Confine.test(:feature).should be_instance_of(Class) + end + + it "should have an 'exists' test" do + Puppet::Provider::Confine.test(:exists).should be_instance_of(Class) + end + + it "should have a 'variable' test" do + Puppet::Provider::Confine.test(:variable).should be_instance_of(Class) + end + + describe "when testing all values" do + before do + @confine = Puppet::Provider::Confine.new(%w{a b c}) + @confine.label = "foo" + end + + it "should be invalid if any values fail" do + @confine.stubs(:pass?).returns true + @confine.expects(:pass?).with("b").returns false + @confine.should_not be_valid + end + + it "should be valid if all values pass" do + @confine.stubs(:pass?).returns true + @confine.should be_valid + end + + it "should short-cut at the first failing value" do + @confine.expects(:pass?).once.returns false + @confine.valid? + end + + it "should log failing confines with the label and message" do + @confine.stubs(:pass?).returns false + @confine.expects(:message).returns "My message" + @confine.expects(:label).returns "Mylabel" + Puppet.expects(:debug).with("Mylabel: My message") + @confine.valid? + end + end + + describe "when testing the result of the values" do + before { @confine = Puppet::Provider::Confine.new(%w{a b c d}) } + + it "should return an array with the result of the test for each value" do + @confine.stubs(:pass?).returns true + @confine.expects(:pass?).with("b").returns false + @confine.expects(:pass?).with("d").returns false + + @confine.result.should == [true, false, true, false] + end + end +end diff --git a/spec/unit/provider/confine_spec_spec.rb b/spec/unit/provider/confine_spec_spec.rb deleted file mode 100755 index 626f79b27..000000000 --- a/spec/unit/provider/confine_spec_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/provider/confine' - -describe Puppet::Provider::Confine do - it "should require a value" do - lambda { Puppet::Provider::Confine.new() }.should raise_error(ArgumentError) - end - - it "should always convert values to an array" do - Puppet::Provider::Confine.new("/some/file").values.should be_instance_of(Array) - end - - it "should have a 'true' test" do - Puppet::Provider::Confine.test(:true).should be_instance_of(Class) - end - - it "should have a 'false' test" do - Puppet::Provider::Confine.test(:false).should be_instance_of(Class) - end - - it "should have a 'feature' test" do - Puppet::Provider::Confine.test(:feature).should be_instance_of(Class) - end - - it "should have an 'exists' test" do - Puppet::Provider::Confine.test(:exists).should be_instance_of(Class) - end - - it "should have a 'variable' test" do - Puppet::Provider::Confine.test(:variable).should be_instance_of(Class) - end - - describe "when testing all values" do - before do - @confine = Puppet::Provider::Confine.new(%w{a b c}) - @confine.label = "foo" - end - - it "should be invalid if any values fail" do - @confine.stubs(:pass?).returns true - @confine.expects(:pass?).with("b").returns false - @confine.should_not be_valid - end - - it "should be valid if all values pass" do - @confine.stubs(:pass?).returns true - @confine.should be_valid - end - - it "should short-cut at the first failing value" do - @confine.expects(:pass?).once.returns false - @confine.valid? - end - - it "should log failing confines with the label and message" do - @confine.stubs(:pass?).returns false - @confine.expects(:message).returns "My message" - @confine.expects(:label).returns "Mylabel" - Puppet.expects(:debug).with("Mylabel: My message") - @confine.valid? - end - end - - describe "when testing the result of the values" do - before { @confine = Puppet::Provider::Confine.new(%w{a b c d}) } - - it "should return an array with the result of the test for each value" do - @confine.stubs(:pass?).returns true - @confine.expects(:pass?).with("b").returns false - @confine.expects(:pass?).with("d").returns false - - @confine.result.should == [true, false, true, false] - end - end -end diff --git a/spec/unit/provider/confiner_spec.rb b/spec/unit/provider/confiner_spec.rb new file mode 100755 index 000000000..0a0d67fb5 --- /dev/null +++ b/spec/unit/provider/confiner_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/confiner' + +describe Puppet::Provider::Confiner do + before do + @object = Object.new + @object.extend(Puppet::Provider::Confiner) + end + + it "should have a method for defining confines" do + @object.should respond_to(:confine) + end + + it "should have a method for returning its confine collection" do + @object.should respond_to(:confine_collection) + end + + it "should have a method for testing suitability" do + @object.should respond_to(:suitable?) + end + + it "should delegate its confine method to its confine collection" do + coll = mock 'collection' + @object.stubs(:confine_collection).returns coll + coll.expects(:confine).with(:foo => :bar, :bee => :baz) + @object.confine(:foo => :bar, :bee => :baz) + end + + it "should create a new confine collection if one does not exist" do + Puppet::Provider::ConfineCollection.expects(:new).with("mylabel").returns "mycoll" + @object.expects(:to_s).returns "mylabel" + @object.confine_collection.should == "mycoll" + end + + it "should reuse the confine collection" do + @object.confine_collection.should equal(@object.confine_collection) + end + + describe "when testing suitability" do + before do + @coll = mock 'collection' + @object.stubs(:confine_collection).returns @coll + end + + it "should return true if the confine collection is valid" do + @coll.expects(:valid?).returns true + @object.should be_suitable + end + + it "should return false if the confine collection is invalid" do + @coll.expects(:valid?).returns false + @object.should_not be_suitable + end + + it "should return the summary of the confine collection if a long result is asked for" do + @coll.expects(:summary).returns "myresult" + @object.suitable?(false).should == "myresult" + end + end +end diff --git a/spec/unit/provider/confiner_spec_spec.rb b/spec/unit/provider/confiner_spec_spec.rb deleted file mode 100755 index 0a0d67fb5..000000000 --- a/spec/unit/provider/confiner_spec_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/provider/confiner' - -describe Puppet::Provider::Confiner do - before do - @object = Object.new - @object.extend(Puppet::Provider::Confiner) - end - - it "should have a method for defining confines" do - @object.should respond_to(:confine) - end - - it "should have a method for returning its confine collection" do - @object.should respond_to(:confine_collection) - end - - it "should have a method for testing suitability" do - @object.should respond_to(:suitable?) - end - - it "should delegate its confine method to its confine collection" do - coll = mock 'collection' - @object.stubs(:confine_collection).returns coll - coll.expects(:confine).with(:foo => :bar, :bee => :baz) - @object.confine(:foo => :bar, :bee => :baz) - end - - it "should create a new confine collection if one does not exist" do - Puppet::Provider::ConfineCollection.expects(:new).with("mylabel").returns "mycoll" - @object.expects(:to_s).returns "mylabel" - @object.confine_collection.should == "mycoll" - end - - it "should reuse the confine collection" do - @object.confine_collection.should equal(@object.confine_collection) - end - - describe "when testing suitability" do - before do - @coll = mock 'collection' - @object.stubs(:confine_collection).returns @coll - end - - it "should return true if the confine collection is valid" do - @coll.expects(:valid?).returns true - @object.should be_suitable - end - - it "should return false if the confine collection is invalid" do - @coll.expects(:valid?).returns false - @object.should_not be_suitable - end - - it "should return the summary of the confine collection if a long result is asked for" do - @coll.expects(:summary).returns "myresult" - @object.suitable?(false).should == "myresult" - end - end -end diff --git a/spec/unit/provider/group/groupadd_spec.rb b/spec/unit/provider/group/groupadd_spec.rb new file mode 100755 index 000000000..fb4b811cb --- /dev/null +++ b/spec/unit/provider/group/groupadd_spec.rb @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:group).provider(:groupadd) + +describe provider_class do + before do + @resource = stub("resource", :name => "mygroup") + @provider = provider_class.new(@resource) + end + + # #1360 + it "should add -o when allowdupe is enabled and the group is being created" do + @resource.stubs(:should).returns "fakeval" + @resource.stubs(:[]).returns "fakeval" + @resource.expects(:allowdupe?).returns true + @provider.expects(:execute).with { |args| args.include?("-o") } + + @provider.create + end + + it "should add -o when allowdupe is enabled and the gid is being modified" do + @resource.stubs(:should).returns "fakeval" + @resource.stubs(:[]).returns "fakeval" + @resource.expects(:allowdupe?).returns true + @provider.expects(:execute).with { |args| args.include?("-o") } + + @provider.gid = 150 + end +end diff --git a/spec/unit/provider/group/groupadd_spec_spec.rb b/spec/unit/provider/group/groupadd_spec_spec.rb deleted file mode 100755 index fb4b811cb..000000000 --- a/spec/unit/provider/group/groupadd_spec_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:group).provider(:groupadd) - -describe provider_class do - before do - @resource = stub("resource", :name => "mygroup") - @provider = provider_class.new(@resource) - end - - # #1360 - it "should add -o when allowdupe is enabled and the group is being created" do - @resource.stubs(:should).returns "fakeval" - @resource.stubs(:[]).returns "fakeval" - @resource.expects(:allowdupe?).returns true - @provider.expects(:execute).with { |args| args.include?("-o") } - - @provider.create - end - - it "should add -o when allowdupe is enabled and the gid is being modified" do - @resource.stubs(:should).returns "fakeval" - @resource.stubs(:[]).returns "fakeval" - @resource.expects(:allowdupe?).returns true - @provider.expects(:execute).with { |args| args.include?("-o") } - - @provider.gid = 150 - end -end diff --git a/spec/unit/provider/group/ldap_spec.rb b/spec/unit/provider/group/ldap_spec.rb new file mode 100755 index 000000000..ab2bd72aa --- /dev/null +++ b/spec/unit/provider/group/ldap_spec.rb @@ -0,0 +1,105 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-10. +# Copyright (c) 2006. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:group).provider(:ldap) + +describe provider_class do + it "should have the Ldap provider class as its baseclass" do + provider_class.superclass.should equal(Puppet::Provider::Ldap) + end + + it "should manage :posixGroup objectclass" do + provider_class.manager.objectclasses.should == [:posixGroup] + end + + it "should use 'ou=Groups' as its relative base" do + provider_class.manager.location.should == "ou=Groups" + end + + it "should use :cn as its rdn" do + provider_class.manager.rdn.should == :cn + end + + it "should map :name to 'cn'" do + provider_class.manager.ldap_name(:name).should == 'cn' + end + + it "should map :gid to 'gidNumber'" do + provider_class.manager.ldap_name(:gid).should == 'gidNumber' + end + + it "should map :members to 'memberUid', to be used by the user ldap provider" do + provider_class.manager.ldap_name(:members).should == 'memberUid' + end + + describe "when being created" do + before do + # So we don't try to actually talk to ldap + @connection = mock 'connection' + provider_class.manager.stubs(:connect).yields @connection + end + + describe "with no gid specified" do + it "should pick the first available GID after the largest existing GID" do + low = {:name=>["luke"], :gid=>["600"]} + high = {:name=>["testing"], :gid=>["640"]} + provider_class.manager.expects(:search).returns([low, high]) + + resource = stub 'resource', :should => %w{whatever} + resource.stubs(:should).with(:gid).returns nil + resource.stubs(:should).with(:ensure).returns :present + instance = provider_class.new(:name => "luke", :ensure => :absent) + instance.stubs(:resource).returns resource + + @connection.expects(:add).with { |dn, attrs| attrs["gidNumber"] == ["641"] } + + instance.create + instance.flush + end + + it "should pick '501' as its GID if no groups are found" do + provider_class.manager.expects(:search).returns nil + + resource = stub 'resource', :should => %w{whatever} + resource.stubs(:should).with(:gid).returns nil + resource.stubs(:should).with(:ensure).returns :present + instance = provider_class.new(:name => "luke", :ensure => :absent) + instance.stubs(:resource).returns resource + + @connection.expects(:add).with { |dn, attrs| attrs["gidNumber"] == ["501"] } + + instance.create + instance.flush + end + end + end + + it "should have a method for converting group names to GIDs" do + provider_class.should respond_to(:name2id) + end + + describe "when converting from a group name to GID" do + it "should use the ldap manager to look up the GID" do + provider_class.manager.expects(:search).with("cn=foo") + provider_class.name2id("foo") + end + + it "should return nil if no group is found" do + provider_class.manager.expects(:search).with("cn=foo").returns nil + provider_class.name2id("foo").should be_nil + provider_class.manager.expects(:search).with("cn=bar").returns [] + provider_class.name2id("bar").should be_nil + end + + # We shouldn't ever actually have more than one gid, but it doesn't hurt + # to test for the possibility. + it "should return the first gid from the first returned group" do + provider_class.manager.expects(:search).with("cn=foo").returns [{:name => "foo", :gid => [10, 11]}, {:name => :bar, :gid => [20, 21]}] + provider_class.name2id("foo").should == 10 + end + end +end diff --git a/spec/unit/provider/group/ldap_spec_spec.rb b/spec/unit/provider/group/ldap_spec_spec.rb deleted file mode 100755 index ab2bd72aa..000000000 --- a/spec/unit/provider/group/ldap_spec_spec.rb +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-10. -# Copyright (c) 2006. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:group).provider(:ldap) - -describe provider_class do - it "should have the Ldap provider class as its baseclass" do - provider_class.superclass.should equal(Puppet::Provider::Ldap) - end - - it "should manage :posixGroup objectclass" do - provider_class.manager.objectclasses.should == [:posixGroup] - end - - it "should use 'ou=Groups' as its relative base" do - provider_class.manager.location.should == "ou=Groups" - end - - it "should use :cn as its rdn" do - provider_class.manager.rdn.should == :cn - end - - it "should map :name to 'cn'" do - provider_class.manager.ldap_name(:name).should == 'cn' - end - - it "should map :gid to 'gidNumber'" do - provider_class.manager.ldap_name(:gid).should == 'gidNumber' - end - - it "should map :members to 'memberUid', to be used by the user ldap provider" do - provider_class.manager.ldap_name(:members).should == 'memberUid' - end - - describe "when being created" do - before do - # So we don't try to actually talk to ldap - @connection = mock 'connection' - provider_class.manager.stubs(:connect).yields @connection - end - - describe "with no gid specified" do - it "should pick the first available GID after the largest existing GID" do - low = {:name=>["luke"], :gid=>["600"]} - high = {:name=>["testing"], :gid=>["640"]} - provider_class.manager.expects(:search).returns([low, high]) - - resource = stub 'resource', :should => %w{whatever} - resource.stubs(:should).with(:gid).returns nil - resource.stubs(:should).with(:ensure).returns :present - instance = provider_class.new(:name => "luke", :ensure => :absent) - instance.stubs(:resource).returns resource - - @connection.expects(:add).with { |dn, attrs| attrs["gidNumber"] == ["641"] } - - instance.create - instance.flush - end - - it "should pick '501' as its GID if no groups are found" do - provider_class.manager.expects(:search).returns nil - - resource = stub 'resource', :should => %w{whatever} - resource.stubs(:should).with(:gid).returns nil - resource.stubs(:should).with(:ensure).returns :present - instance = provider_class.new(:name => "luke", :ensure => :absent) - instance.stubs(:resource).returns resource - - @connection.expects(:add).with { |dn, attrs| attrs["gidNumber"] == ["501"] } - - instance.create - instance.flush - end - end - end - - it "should have a method for converting group names to GIDs" do - provider_class.should respond_to(:name2id) - end - - describe "when converting from a group name to GID" do - it "should use the ldap manager to look up the GID" do - provider_class.manager.expects(:search).with("cn=foo") - provider_class.name2id("foo") - end - - it "should return nil if no group is found" do - provider_class.manager.expects(:search).with("cn=foo").returns nil - provider_class.name2id("foo").should be_nil - provider_class.manager.expects(:search).with("cn=bar").returns [] - provider_class.name2id("bar").should be_nil - end - - # We shouldn't ever actually have more than one gid, but it doesn't hurt - # to test for the possibility. - it "should return the first gid from the first returned group" do - provider_class.manager.expects(:search).with("cn=foo").returns [{:name => "foo", :gid => [10, 11]}, {:name => :bar, :gid => [20, 21]}] - provider_class.name2id("foo").should == 10 - end - end -end diff --git a/spec/unit/provider/ldap_spec.rb b/spec/unit/provider/ldap_spec.rb new file mode 100755 index 000000000..6c5820883 --- /dev/null +++ b/spec/unit/provider/ldap_spec.rb @@ -0,0 +1,248 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-21. +# Copyright (c) 2006. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/ldap' + +describe Puppet::Provider::Ldap do + before do + @class = Class.new(Puppet::Provider::Ldap) + end + + it "should be able to define its manager" do + manager = mock 'manager' + Puppet::Util::Ldap::Manager.expects(:new).returns manager + @class.stubs :mk_resource_methods + manager.expects(:manages).with(:one) + @class.manages(:one).should equal(manager) + @class.manager.should equal(manager) + end + + it "should be able to prefetch instances from ldap" do + @class.should respond_to(:prefetch) + end + + it "should create its resource getter/setter methods when the manager is defined" do + manager = mock 'manager' + Puppet::Util::Ldap::Manager.expects(:new).returns manager + @class.expects :mk_resource_methods + manager.stubs(:manages) + @class.manages(:one).should equal(manager) + end + + it "should have an instances method" do + @class.should respond_to(:instances) + end + + describe "when providing a list of instances" do + it "should convert all results returned from the manager's :search method into provider instances" do + manager = mock 'manager' + @class.stubs(:manager).returns manager + + manager.expects(:search).returns %w{one two three} + + @class.expects(:new).with("one").returns(1) + @class.expects(:new).with("two").returns(2) + @class.expects(:new).with("three").returns(3) + + @class.instances.should == [1,2,3] + end + end + + it "should have a prefetch method" do + @class.should respond_to(:prefetch) + end + + describe "when prefetching" do + before do + @manager = mock 'manager' + @class.stubs(:manager).returns @manager + + @resource = mock 'resource' + + @resources = {"one" => @resource} + end + + it "should find an entry for each passed resource" do + @manager.expects(:find).with("one").returns nil + + @class.stubs(:new) + @resource.stubs(:provider=) + @class.prefetch(@resources) + end + + describe "resources that do not exist" do + it "should create a provider with :ensure => :absent" do + result = mock 'result' + @manager.expects(:find).with("one").returns nil + + @class.expects(:new).with(:ensure => :absent).returns "myprovider" + + @resource.expects(:provider=).with("myprovider") + + @class.prefetch(@resources) + end + end + + describe "resources that exist" do + it "should create a provider with the results of the find" do + @manager.expects(:find).with("one").returns("one" => "two") + + @class.expects(:new).with("one" => "two", :ensure => :present).returns "myprovider" + + @resource.expects(:provider=).with("myprovider") + + @class.prefetch(@resources) + end + + it "should set :ensure to :present in the returned values" do + @manager.expects(:find).with("one").returns("one" => "two") + + @class.expects(:new).with("one" => "two", :ensure => :present).returns "myprovider" + + @resource.expects(:provider=).with("myprovider") + + @class.prefetch(@resources) + end + end + end + + describe "when being initialized" do + it "should fail if no manager has been defined" do + lambda { @class.new }.should raise_error(Puppet::DevError) + end + + it "should fail if the manager is invalid" do + manager = stub "manager", :valid? => false + @class.stubs(:manager).returns manager + lambda { @class.new }.should raise_error(Puppet::DevError) + end + + describe "with a hash" do + before do + @manager = stub "manager", :valid? => true + @class.stubs(:manager).returns @manager + + @resource_class = mock 'resource_class' + @class.stubs(:resource_type).returns @resource_class + + @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property + @resource_class.stubs(:attrclass).with(:one).returns(@property_class) + @resource_class.stubs(:valid_parameter?).returns true + end + + it "should store a copy of the hash as its ldap_properties" do + instance = @class.new(:one => :two) + instance.ldap_properties.should == {:one => :two} + end + + it "should only store the first value of each value array for those attributes that do not match all values" do + @property_class.expects(:array_matching).returns :first + instance = @class.new(:one => %w{two three}) + instance.properties.should == {:one => "two"} + end + + it "should store the whole value array for those attributes that match all values" do + @property_class.expects(:array_matching).returns :all + instance = @class.new(:one => %w{two three}) + instance.properties.should == {:one => %w{two three}} + end + + it "should only use the first value for attributes that are not properties" do + # Yay. hackish, but easier than mocking everything. + @resource_class.expects(:attrclass).with(:a).returns Puppet::Type.type(:user).attrclass(:name) + @property_class.stubs(:array_matching).returns :all + + instance = @class.new(:one => %w{two three}, :a => %w{b c}) + instance.properties.should == {:one => %w{two three}, :a => "b"} + end + + it "should discard any properties not valid in the resource class" do + @resource_class.expects(:valid_parameter?).with(:a).returns false + @property_class.stubs(:array_matching).returns :all + + instance = @class.new(:one => %w{two three}, :a => %w{b}) + instance.properties.should == {:one => %w{two three}} + end + end + end + + describe "when an instance" do + before do + @manager = stub "manager", :valid? => true + @class.stubs(:manager).returns @manager + @instance = @class.new + + @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property + @resource_class = stub 'resource_class', :attrclass => @property_class, :valid_parameter? => true, :validproperties => [:one, :two] + @class.stubs(:resource_type).returns @resource_class + end + + it "should have a method for creating the ldap entry" do + @instance.should respond_to(:create) + end + + it "should have a method for removing the ldap entry" do + @instance.should respond_to(:delete) + end + + it "should have a method for returning the class's manager" do + @instance.manager.should equal(@manager) + end + + it "should indicate when the ldap entry already exists" do + @instance = @class.new(:ensure => :present) + @instance.exists?.should be_true + end + + it "should indicate when the ldap entry does not exist" do + @instance = @class.new(:ensure => :absent) + @instance.exists?.should be_false + end + + describe "is being flushed" do + it "should call the manager's :update method with its name, current attributes, and desired attributes" do + @instance.stubs(:name).returns "myname" + @instance.stubs(:ldap_properties).returns(:one => :two) + @instance.stubs(:properties).returns(:three => :four) + @manager.expects(:update).with(@instance.name, {:one => :two}, {:three => :four}) + @instance.flush + end + end + + describe "is being created" do + before do + @rclass = mock 'resource_class' + @rclass.stubs(:validproperties).returns([:one, :two]) + @resource = mock 'resource' + @resource.stubs(:class).returns @rclass + @resource.stubs(:should).returns nil + @instance.stubs(:resource).returns @resource + end + + it "should set its :ensure value to :present" do + @instance.create + @instance.properties[:ensure].should == :present + end + + it "should set all of the other attributes from the resource" do + @resource.expects(:should).with(:one).returns "oneval" + @resource.expects(:should).with(:two).returns "twoval" + + @instance.create + @instance.properties[:one].should == "oneval" + @instance.properties[:two].should == "twoval" + end + end + + describe "is being deleted" do + it "should set its :ensure value to :absent" do + @instance.delete + @instance.properties[:ensure].should == :absent + end + end + end +end diff --git a/spec/unit/provider/ldap_spec_spec.rb b/spec/unit/provider/ldap_spec_spec.rb deleted file mode 100755 index 6c5820883..000000000 --- a/spec/unit/provider/ldap_spec_spec.rb +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-21. -# Copyright (c) 2006. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/provider/ldap' - -describe Puppet::Provider::Ldap do - before do - @class = Class.new(Puppet::Provider::Ldap) - end - - it "should be able to define its manager" do - manager = mock 'manager' - Puppet::Util::Ldap::Manager.expects(:new).returns manager - @class.stubs :mk_resource_methods - manager.expects(:manages).with(:one) - @class.manages(:one).should equal(manager) - @class.manager.should equal(manager) - end - - it "should be able to prefetch instances from ldap" do - @class.should respond_to(:prefetch) - end - - it "should create its resource getter/setter methods when the manager is defined" do - manager = mock 'manager' - Puppet::Util::Ldap::Manager.expects(:new).returns manager - @class.expects :mk_resource_methods - manager.stubs(:manages) - @class.manages(:one).should equal(manager) - end - - it "should have an instances method" do - @class.should respond_to(:instances) - end - - describe "when providing a list of instances" do - it "should convert all results returned from the manager's :search method into provider instances" do - manager = mock 'manager' - @class.stubs(:manager).returns manager - - manager.expects(:search).returns %w{one two three} - - @class.expects(:new).with("one").returns(1) - @class.expects(:new).with("two").returns(2) - @class.expects(:new).with("three").returns(3) - - @class.instances.should == [1,2,3] - end - end - - it "should have a prefetch method" do - @class.should respond_to(:prefetch) - end - - describe "when prefetching" do - before do - @manager = mock 'manager' - @class.stubs(:manager).returns @manager - - @resource = mock 'resource' - - @resources = {"one" => @resource} - end - - it "should find an entry for each passed resource" do - @manager.expects(:find).with("one").returns nil - - @class.stubs(:new) - @resource.stubs(:provider=) - @class.prefetch(@resources) - end - - describe "resources that do not exist" do - it "should create a provider with :ensure => :absent" do - result = mock 'result' - @manager.expects(:find).with("one").returns nil - - @class.expects(:new).with(:ensure => :absent).returns "myprovider" - - @resource.expects(:provider=).with("myprovider") - - @class.prefetch(@resources) - end - end - - describe "resources that exist" do - it "should create a provider with the results of the find" do - @manager.expects(:find).with("one").returns("one" => "two") - - @class.expects(:new).with("one" => "two", :ensure => :present).returns "myprovider" - - @resource.expects(:provider=).with("myprovider") - - @class.prefetch(@resources) - end - - it "should set :ensure to :present in the returned values" do - @manager.expects(:find).with("one").returns("one" => "two") - - @class.expects(:new).with("one" => "two", :ensure => :present).returns "myprovider" - - @resource.expects(:provider=).with("myprovider") - - @class.prefetch(@resources) - end - end - end - - describe "when being initialized" do - it "should fail if no manager has been defined" do - lambda { @class.new }.should raise_error(Puppet::DevError) - end - - it "should fail if the manager is invalid" do - manager = stub "manager", :valid? => false - @class.stubs(:manager).returns manager - lambda { @class.new }.should raise_error(Puppet::DevError) - end - - describe "with a hash" do - before do - @manager = stub "manager", :valid? => true - @class.stubs(:manager).returns @manager - - @resource_class = mock 'resource_class' - @class.stubs(:resource_type).returns @resource_class - - @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property - @resource_class.stubs(:attrclass).with(:one).returns(@property_class) - @resource_class.stubs(:valid_parameter?).returns true - end - - it "should store a copy of the hash as its ldap_properties" do - instance = @class.new(:one => :two) - instance.ldap_properties.should == {:one => :two} - end - - it "should only store the first value of each value array for those attributes that do not match all values" do - @property_class.expects(:array_matching).returns :first - instance = @class.new(:one => %w{two three}) - instance.properties.should == {:one => "two"} - end - - it "should store the whole value array for those attributes that match all values" do - @property_class.expects(:array_matching).returns :all - instance = @class.new(:one => %w{two three}) - instance.properties.should == {:one => %w{two three}} - end - - it "should only use the first value for attributes that are not properties" do - # Yay. hackish, but easier than mocking everything. - @resource_class.expects(:attrclass).with(:a).returns Puppet::Type.type(:user).attrclass(:name) - @property_class.stubs(:array_matching).returns :all - - instance = @class.new(:one => %w{two three}, :a => %w{b c}) - instance.properties.should == {:one => %w{two three}, :a => "b"} - end - - it "should discard any properties not valid in the resource class" do - @resource_class.expects(:valid_parameter?).with(:a).returns false - @property_class.stubs(:array_matching).returns :all - - instance = @class.new(:one => %w{two three}, :a => %w{b}) - instance.properties.should == {:one => %w{two three}} - end - end - end - - describe "when an instance" do - before do - @manager = stub "manager", :valid? => true - @class.stubs(:manager).returns @manager - @instance = @class.new - - @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property - @resource_class = stub 'resource_class', :attrclass => @property_class, :valid_parameter? => true, :validproperties => [:one, :two] - @class.stubs(:resource_type).returns @resource_class - end - - it "should have a method for creating the ldap entry" do - @instance.should respond_to(:create) - end - - it "should have a method for removing the ldap entry" do - @instance.should respond_to(:delete) - end - - it "should have a method for returning the class's manager" do - @instance.manager.should equal(@manager) - end - - it "should indicate when the ldap entry already exists" do - @instance = @class.new(:ensure => :present) - @instance.exists?.should be_true - end - - it "should indicate when the ldap entry does not exist" do - @instance = @class.new(:ensure => :absent) - @instance.exists?.should be_false - end - - describe "is being flushed" do - it "should call the manager's :update method with its name, current attributes, and desired attributes" do - @instance.stubs(:name).returns "myname" - @instance.stubs(:ldap_properties).returns(:one => :two) - @instance.stubs(:properties).returns(:three => :four) - @manager.expects(:update).with(@instance.name, {:one => :two}, {:three => :four}) - @instance.flush - end - end - - describe "is being created" do - before do - @rclass = mock 'resource_class' - @rclass.stubs(:validproperties).returns([:one, :two]) - @resource = mock 'resource' - @resource.stubs(:class).returns @rclass - @resource.stubs(:should).returns nil - @instance.stubs(:resource).returns @resource - end - - it "should set its :ensure value to :present" do - @instance.create - @instance.properties[:ensure].should == :present - end - - it "should set all of the other attributes from the resource" do - @resource.expects(:should).with(:one).returns "oneval" - @resource.expects(:should).with(:two).returns "twoval" - - @instance.create - @instance.properties[:one].should == "oneval" - @instance.properties[:two].should == "twoval" - end - end - - describe "is being deleted" do - it "should set its :ensure value to :absent" do - @instance.delete - @instance.properties[:ensure].should == :absent - end - end - end -end diff --git a/spec/unit/provider/macauthorization_spec.rb b/spec/unit/provider/macauthorization_spec.rb new file mode 100755 index 000000000..8e0ba2456 --- /dev/null +++ b/spec/unit/provider/macauthorization_spec.rb @@ -0,0 +1,147 @@ +#!/usr/bin/env ruby +# +# Unit testing for the macauthorization provider +# + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet' +require 'facter/util/plist' + +provider_class = Puppet::Type.type(:macauthorization).provider(:macauthorization) + +describe provider_class do + + before :each do + # Create a mock resource + @resource = stub 'resource' + + @authname = "foo.spam.eggs.puppettest" + @authplist = {} + + @rules = {@authname => @authplist} + + authdb = {} + authdb["rules"] = { "foorule" => "foo" } + authdb["rights"] = { "fooright" => "foo" } + + # Stub out Plist::parse_xml + Plist.stubs(:parse_xml).returns(authdb) + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name, ensure + @resource.stubs(:[]).with(:name).returns @authname + @resource.stubs(:[]).with(:ensure).returns :present + @resource.stubs(:ref).returns "MacAuthorization[#{@authname}]" + + @provider = provider_class.new(@resource) + end + + it "should have a create method" do + @provider.should respond_to(:create) + end + + it "should have a destroy method" do + @provider.should respond_to(:destroy) + end + + it "should have an exists? method" do + @provider.should respond_to(:exists?) + end + + it "should have a flush method" do + @provider.should respond_to(:flush) + end + + properties = [ :allow_root, :authenticate_user, :auth_class, :comment, + :group, :k_of_n, :mechanisms, :rule, :session_owner, + :shared, :timeout, :tries, :auth_type ] + + properties.each do |prop| + it "should have a #{prop.to_s} method" do + @provider.should respond_to(prop.to_s) + end + + it "should have a #{prop.to_s}= method" do + @provider.should respond_to(prop.to_s + "=") + end + end + + describe "when destroying a right" do + before :each do + @resource.stubs(:[]).with(:auth_type).returns(:right) + end + + it "should call the internal method destroy_right" do + @provider.expects(:destroy_right) + @provider.destroy + end + it "should call the external command 'security authorizationdb remove @authname" do + @provider.expects(:security).with("authorizationdb", :remove, @authname) + @provider.destroy + end + end + + describe "when destroying a rule" do + before :each do + @resource.stubs(:[]).with(:auth_type).returns(:rule) + end + + it "should call the internal method destroy_rule" do + @provider.expects(:destroy_rule) + @provider.destroy + end + end + + describe "when flushing a right" do + before :each do + @resource.stubs(:[]).with(:auth_type).returns(:right) + end + + it "should call the internal method flush_right" do + @provider.expects(:flush_right) + @provider.flush + end + + it "should call the internal method set_right" do + @provider.expects(:set_right) + @provider.flush + end + + it "should read and write to the auth database with the right arguments" do + @provider.expects(:execute).with() { |cmds, args| + cmds.include?("read") and + cmds.include?(@authname) and + args[:combine] == false + }.once + + @provider.expects(:execute).with() { |cmds, args| + cmds.include?("write") and + cmds.include?(@authname) and + args[:combine] == false and + args[:stdinfile] != nil + }.once + @provider.flush + end + + end + + describe "when flushing a rule" do + before :each do + @resource.stubs(:[]).with(:auth_type).returns(:rule) + end + + it "should call the internal method flush_rule" do + @provider.expects(:flush_rule) + @provider.flush + end + + it "should call the internal method set_rule" do + @provider.expects(:set_rule) + @provider.flush + end + end + +end diff --git a/spec/unit/provider/macauthorization_spec_spec.rb b/spec/unit/provider/macauthorization_spec_spec.rb deleted file mode 100755 index 8e0ba2456..000000000 --- a/spec/unit/provider/macauthorization_spec_spec.rb +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env ruby -# -# Unit testing for the macauthorization provider -# - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet' -require 'facter/util/plist' - -provider_class = Puppet::Type.type(:macauthorization).provider(:macauthorization) - -describe provider_class do - - before :each do - # Create a mock resource - @resource = stub 'resource' - - @authname = "foo.spam.eggs.puppettest" - @authplist = {} - - @rules = {@authname => @authplist} - - authdb = {} - authdb["rules"] = { "foorule" => "foo" } - authdb["rights"] = { "fooright" => "foo" } - - # Stub out Plist::parse_xml - Plist.stubs(:parse_xml).returns(authdb) - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name, ensure - @resource.stubs(:[]).with(:name).returns @authname - @resource.stubs(:[]).with(:ensure).returns :present - @resource.stubs(:ref).returns "MacAuthorization[#{@authname}]" - - @provider = provider_class.new(@resource) - end - - it "should have a create method" do - @provider.should respond_to(:create) - end - - it "should have a destroy method" do - @provider.should respond_to(:destroy) - end - - it "should have an exists? method" do - @provider.should respond_to(:exists?) - end - - it "should have a flush method" do - @provider.should respond_to(:flush) - end - - properties = [ :allow_root, :authenticate_user, :auth_class, :comment, - :group, :k_of_n, :mechanisms, :rule, :session_owner, - :shared, :timeout, :tries, :auth_type ] - - properties.each do |prop| - it "should have a #{prop.to_s} method" do - @provider.should respond_to(prop.to_s) - end - - it "should have a #{prop.to_s}= method" do - @provider.should respond_to(prop.to_s + "=") - end - end - - describe "when destroying a right" do - before :each do - @resource.stubs(:[]).with(:auth_type).returns(:right) - end - - it "should call the internal method destroy_right" do - @provider.expects(:destroy_right) - @provider.destroy - end - it "should call the external command 'security authorizationdb remove @authname" do - @provider.expects(:security).with("authorizationdb", :remove, @authname) - @provider.destroy - end - end - - describe "when destroying a rule" do - before :each do - @resource.stubs(:[]).with(:auth_type).returns(:rule) - end - - it "should call the internal method destroy_rule" do - @provider.expects(:destroy_rule) - @provider.destroy - end - end - - describe "when flushing a right" do - before :each do - @resource.stubs(:[]).with(:auth_type).returns(:right) - end - - it "should call the internal method flush_right" do - @provider.expects(:flush_right) - @provider.flush - end - - it "should call the internal method set_right" do - @provider.expects(:set_right) - @provider.flush - end - - it "should read and write to the auth database with the right arguments" do - @provider.expects(:execute).with() { |cmds, args| - cmds.include?("read") and - cmds.include?(@authname) and - args[:combine] == false - }.once - - @provider.expects(:execute).with() { |cmds, args| - cmds.include?("write") and - cmds.include?(@authname) and - args[:combine] == false and - args[:stdinfile] != nil - }.once - @provider.flush - end - - end - - describe "when flushing a rule" do - before :each do - @resource.stubs(:[]).with(:auth_type).returns(:rule) - end - - it "should call the internal method flush_rule" do - @provider.expects(:flush_rule) - @provider.flush - end - - it "should call the internal method set_rule" do - @provider.expects(:set_rule) - @provider.flush - end - end - -end diff --git a/spec/unit/provider/mcx/mcxcontent_spec.rb b/spec/unit/provider/mcx/mcxcontent_spec.rb new file mode 100755 index 000000000..d8f431afe --- /dev/null +++ b/spec/unit/provider/mcx/mcxcontent_spec.rb @@ -0,0 +1,175 @@ +#! /usr/bin/env ruby +#-- +# Copyright (C) 2008 Jeffrey J McCune. + +# This program and entire repository is free software; you can +# redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software +# Foundation; either version 2 of the License, or any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Author: Jeff McCune + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:mcx).provider(:mcxcontent) + +# describe creates a new ExampleGroup object. +describe provider_class do + + # :each executes before each test. + # :all executes once for the test group and before :each. + before :each do + # Create a mock resource + @resource = stub 'resource' + + @provider = provider_class.new + @attached_to = "/Users/foobar" + @ds_path = "/Local/Default/Users/foobar" + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name, ensure and enable + @resource.stubs(:[]).with(:name).returns @attached_to + @resource.stubs(:[]).with(:ensure).returns :present + @resource.stubs(:ref).returns "Mcx[#{@attached_to}]" + + # stub out the provider methods that actually touch the filesystem + # or execute commands + @provider.class.stubs(:execute).returns('') + @provider.stubs(:execute).returns('') + @provider.stubs(:resource).returns @resource + end + + it "should have a create method." do + @provider.should respond_to(:create) + end + + it "should have a destroy method." do + @provider.should respond_to(:destroy) + end + + it "should have an exists? method." do + @provider.should respond_to(:exists?) + end + + it "should have an content method." do + @provider.should respond_to(:content) + end + + it "should have an content= method." do + @provider.should respond_to(:content=) + end + + describe "when managing the resource" do + it "should execute external command dscl from :create" do + @provider.class.expects(:dscl).returns('').once + @provider.create + end + it "should execute external command dscl from :destroy" do + @provider.class.expects(:dscl).with('localhost', '-mcxdelete', @ds_path).returns('').once + @provider.destroy + end + it "should execute external command dscl from :exists?" do + @provider.class.expects(:dscl).with('localhost', '-mcxexport', @ds_path).returns('').once + @provider.exists? + end + it "should execute external command dscl from :content" do + @provider.class.expects(:dscl).with('localhost', '-mcxexport', @ds_path).returns('') + @provider.content + end + it "should execute external command dscl from :content=" do + @provider.class.expects(:dscl).returns('') + @provider.content='' + end + end + + describe "when creating and parsing the name for ds_type" do + before :each do + @resource.stubs(:[]).with(:name).returns "/Foo/bar" + end + it "should not accept /Foo/bar" do + lambda { @provider.create }.should raise_error(MCXContentProviderException) + end + it "should accept /Foo/bar with ds_type => user" do + @resource.stubs(:[]).with(:ds_type).returns "user" + lambda { @provider.create }.should_not raise_error(MCXContentProviderException) + end + it "should accept /Foo/bar with ds_type => group" do + @resource.stubs(:[]).with(:ds_type).returns "group" + lambda { @provider.create }.should_not raise_error(MCXContentProviderException) + end + it "should accept /Foo/bar with ds_type => computer" do + @resource.stubs(:[]).with(:ds_type).returns "computer" + lambda { @provider.create }.should_not raise_error(MCXContentProviderException) + end + it "should accept :name => /Foo/bar with ds_type => computerlist" do + @resource.stubs(:[]).with(:ds_type).returns "computerlist" + lambda { @provider.create }.should_not raise_error(MCXContentProviderException) + end + end + + describe "when creating and :name => foobar" do + before :each do + @resource.stubs(:[]).with(:name).returns "foobar" + end + it "should not accept unspecified :ds_type and :ds_name" do + lambda { @provider.create }.should raise_error(MCXContentProviderException) + end + it "should not accept unspecified :ds_type" do + @resource.stubs(:[]).with(:ds_type).returns "user" + lambda { @provider.create }.should raise_error(MCXContentProviderException) + end + it "should not accept unspecified :ds_name" do + @resource.stubs(:[]).with(:ds_name).returns "foo" + lambda { @provider.create }.should raise_error(MCXContentProviderException) + end + it "should accept :ds_type => user, ds_name => foo" do + @resource.stubs(:[]).with(:ds_type).returns "user" + @resource.stubs(:[]).with(:ds_name).returns "foo" + lambda { @provider.create }.should_not raise_error(MCXContentProviderException) + end + it "should accept :ds_type => group, ds_name => foo" do + @resource.stubs(:[]).with(:ds_type).returns "group" + @resource.stubs(:[]).with(:ds_name).returns "foo" + lambda { @provider.create }.should_not raise_error(MCXContentProviderException) + end + it "should accept :ds_type => computer, ds_name => foo" do + @resource.stubs(:[]).with(:ds_type).returns "computer" + @resource.stubs(:[]).with(:ds_name).returns "foo" + lambda { @provider.create }.should_not raise_error(MCXContentProviderException) + end + it "should accept :ds_type => computerlist, ds_name => foo" do + @resource.stubs(:[]).with(:ds_type).returns "computerlist" + @resource.stubs(:[]).with(:ds_name).returns "foo" + lambda { @provider.create }.should_not raise_error(MCXContentProviderException) + end + it "should not accept :ds_type => bogustype, ds_name => foo" do + @resource.stubs(:[]).with(:ds_type).returns "bogustype" + @resource.stubs(:[]).with(:ds_name).returns "foo" + lambda { @provider.create }.should raise_error(MCXContentProviderException) + end + end + + describe "when gathering existing instances" do + it "should define an instances class method." do + @provider.class.should respond_to(:instances) + end + it "should call external command dscl -list /Local/Default/ on each known ds_type" do + @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Users").returns('') + @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Groups").returns('') + @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Computers").returns('') + @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/ComputerLists").returns('') + @provider.class.instances + end + end +end diff --git a/spec/unit/provider/mcx/mcxcontent_spec_spec.rb b/spec/unit/provider/mcx/mcxcontent_spec_spec.rb deleted file mode 100755 index d8f431afe..000000000 --- a/spec/unit/provider/mcx/mcxcontent_spec_spec.rb +++ /dev/null @@ -1,175 +0,0 @@ -#! /usr/bin/env ruby -#-- -# Copyright (C) 2008 Jeffrey J McCune. - -# This program and entire repository is free software; you can -# redistribute it and/or modify it under the terms of the GNU -# General Public License as published by the Free Software -# Foundation; either version 2 of the License, or any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -# Author: Jeff McCune - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:mcx).provider(:mcxcontent) - -# describe creates a new ExampleGroup object. -describe provider_class do - - # :each executes before each test. - # :all executes once for the test group and before :each. - before :each do - # Create a mock resource - @resource = stub 'resource' - - @provider = provider_class.new - @attached_to = "/Users/foobar" - @ds_path = "/Local/Default/Users/foobar" - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name, ensure and enable - @resource.stubs(:[]).with(:name).returns @attached_to - @resource.stubs(:[]).with(:ensure).returns :present - @resource.stubs(:ref).returns "Mcx[#{@attached_to}]" - - # stub out the provider methods that actually touch the filesystem - # or execute commands - @provider.class.stubs(:execute).returns('') - @provider.stubs(:execute).returns('') - @provider.stubs(:resource).returns @resource - end - - it "should have a create method." do - @provider.should respond_to(:create) - end - - it "should have a destroy method." do - @provider.should respond_to(:destroy) - end - - it "should have an exists? method." do - @provider.should respond_to(:exists?) - end - - it "should have an content method." do - @provider.should respond_to(:content) - end - - it "should have an content= method." do - @provider.should respond_to(:content=) - end - - describe "when managing the resource" do - it "should execute external command dscl from :create" do - @provider.class.expects(:dscl).returns('').once - @provider.create - end - it "should execute external command dscl from :destroy" do - @provider.class.expects(:dscl).with('localhost', '-mcxdelete', @ds_path).returns('').once - @provider.destroy - end - it "should execute external command dscl from :exists?" do - @provider.class.expects(:dscl).with('localhost', '-mcxexport', @ds_path).returns('').once - @provider.exists? - end - it "should execute external command dscl from :content" do - @provider.class.expects(:dscl).with('localhost', '-mcxexport', @ds_path).returns('') - @provider.content - end - it "should execute external command dscl from :content=" do - @provider.class.expects(:dscl).returns('') - @provider.content='' - end - end - - describe "when creating and parsing the name for ds_type" do - before :each do - @resource.stubs(:[]).with(:name).returns "/Foo/bar" - end - it "should not accept /Foo/bar" do - lambda { @provider.create }.should raise_error(MCXContentProviderException) - end - it "should accept /Foo/bar with ds_type => user" do - @resource.stubs(:[]).with(:ds_type).returns "user" - lambda { @provider.create }.should_not raise_error(MCXContentProviderException) - end - it "should accept /Foo/bar with ds_type => group" do - @resource.stubs(:[]).with(:ds_type).returns "group" - lambda { @provider.create }.should_not raise_error(MCXContentProviderException) - end - it "should accept /Foo/bar with ds_type => computer" do - @resource.stubs(:[]).with(:ds_type).returns "computer" - lambda { @provider.create }.should_not raise_error(MCXContentProviderException) - end - it "should accept :name => /Foo/bar with ds_type => computerlist" do - @resource.stubs(:[]).with(:ds_type).returns "computerlist" - lambda { @provider.create }.should_not raise_error(MCXContentProviderException) - end - end - - describe "when creating and :name => foobar" do - before :each do - @resource.stubs(:[]).with(:name).returns "foobar" - end - it "should not accept unspecified :ds_type and :ds_name" do - lambda { @provider.create }.should raise_error(MCXContentProviderException) - end - it "should not accept unspecified :ds_type" do - @resource.stubs(:[]).with(:ds_type).returns "user" - lambda { @provider.create }.should raise_error(MCXContentProviderException) - end - it "should not accept unspecified :ds_name" do - @resource.stubs(:[]).with(:ds_name).returns "foo" - lambda { @provider.create }.should raise_error(MCXContentProviderException) - end - it "should accept :ds_type => user, ds_name => foo" do - @resource.stubs(:[]).with(:ds_type).returns "user" - @resource.stubs(:[]).with(:ds_name).returns "foo" - lambda { @provider.create }.should_not raise_error(MCXContentProviderException) - end - it "should accept :ds_type => group, ds_name => foo" do - @resource.stubs(:[]).with(:ds_type).returns "group" - @resource.stubs(:[]).with(:ds_name).returns "foo" - lambda { @provider.create }.should_not raise_error(MCXContentProviderException) - end - it "should accept :ds_type => computer, ds_name => foo" do - @resource.stubs(:[]).with(:ds_type).returns "computer" - @resource.stubs(:[]).with(:ds_name).returns "foo" - lambda { @provider.create }.should_not raise_error(MCXContentProviderException) - end - it "should accept :ds_type => computerlist, ds_name => foo" do - @resource.stubs(:[]).with(:ds_type).returns "computerlist" - @resource.stubs(:[]).with(:ds_name).returns "foo" - lambda { @provider.create }.should_not raise_error(MCXContentProviderException) - end - it "should not accept :ds_type => bogustype, ds_name => foo" do - @resource.stubs(:[]).with(:ds_type).returns "bogustype" - @resource.stubs(:[]).with(:ds_name).returns "foo" - lambda { @provider.create }.should raise_error(MCXContentProviderException) - end - end - - describe "when gathering existing instances" do - it "should define an instances class method." do - @provider.class.should respond_to(:instances) - end - it "should call external command dscl -list /Local/Default/ on each known ds_type" do - @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Users").returns('') - @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Groups").returns('') - @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Computers").returns('') - @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/ComputerLists").returns('') - @provider.class.instances - end - end -end diff --git a/spec/unit/provider/mount/parsed_spec.rb b/spec/unit/provider/mount/parsed_spec.rb new file mode 100755 index 000000000..0cf206d4b --- /dev/null +++ b/spec/unit/provider/mount/parsed_spec.rb @@ -0,0 +1,195 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-9-12. +# Copyright (c) 2006. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppettest/support/utils' +require 'puppettest/fileparsing' + +module ParsedMountTesting + include PuppetTest::Support::Utils + include PuppetTest::FileParsing + + def fake_fstab + os = Facter['operatingsystem'] + if os == "Solaris" + name = "solaris.fstab" + elsif os == "FreeBSD" + name = "freebsd.fstab" + else + # Catchall for other fstabs + name = "linux.fstab" + end + oldpath = @provider_class.default_target + return fakefile(File::join("data/types/mount", name)) + end + + def mkmountargs + mount = nil + + if defined? @pcount + @pcount += 1 + else + @pcount = 1 + end + args = { + :name => "/fspuppet%s" % @pcount, + :device => "/dev/dsk%s" % @pcount, + } + + @provider_class.fields(:parsed).each do |field| + unless args.include? field + args[field] = "fake%s%s" % [field, @pcount] + end + end + + return args + end + + def mkmount + hash = mkmountargs() + #hash[:provider] = @provider_class.name + + fakeresource = stub :type => :mount, :name => hash[:name] + fakeresource.stubs(:[]).with(:name).returns(hash[:name]) + fakeresource.stubs(:should).with(:target).returns(nil) + + mount = @provider_class.new(fakeresource) + hash[:record_type] = :parsed + hash[:ensure] = :present + mount.property_hash = hash + + return mount + end + + # Here we just create a fake host type that answers to all of the methods + # but does not modify our actual system. + def mkfaketype + @provider.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) + end +end + +provider_class = Puppet::Type.type(:mount).provider(:parsed) + +describe provider_class do + before :each do + @mount_class = Puppet::Type.type(:mount) + @provider_class = @mount_class.provider(:parsed) + end + + + describe provider_class do + include ParsedMountTesting + + it "should be able to parse all of the example mount tabs" do + tab = fake_fstab + @provider = @provider_class + + # LAK:FIXME Again, a relatively bad test, but I don't know how to rspec-ify this. + # I suppose this is more of an integration test? I dunno. + fakedataparse(tab) do + # Now just make we've got some mounts we know will be there + hashes = @provider_class.target_records(tab).find_all { |i| i.is_a? Hash } + (hashes.length > 0).should be_true + root = hashes.find { |i| i[:name] == "/" } + + proc { @provider_class.to_file(hashes) }.should_not raise_error + end + end + + # LAK:FIXME I can't mock Facter because this test happens at parse-time. + it "should default to /etc/vfstab on Solaris and /etc/fstab everywhere else" do + should = case Facter.value(:operatingsystem) + when "Solaris"; "/etc/vfstab" + else + "/etc/fstab" + end + Puppet::Type.type(:mount).provider(:parsed).default_target.should == should + end + + it "should not crash on incomplete lines in fstab" do + parse = @provider_class.parse <<-FSTAB +/dev/incomplete +/dev/device name + FSTAB + + lambda{ @provider_class.to_line(parse[0]) }.should_not raise_error + end + end + + describe provider_class, " when mounting an absent filesystem" do + include ParsedMountTesting + + # #730 - Make sure 'flush' is called when a mount is moving from absent to mounted + it "should flush the fstab to disk" do + mount = mkmount + + # Mark the mount as absent + mount.property_hash[:ensure] = :absent + + mount.stubs(:mountcmd) # just so we don't actually try to mount anything + + mount.expects(:flush) + mount.mount + end + end + + describe provider_class, " when modifying the filesystem tab" do + include ParsedMountTesting + before do + Puppet.settings.stubs(:use) + # Never write to disk, only to RAM. + #@provider_class.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) + @provider_class.stubs(:target_object).returns(Puppet::Util::FileType.filetype(:ram).new("eh")) + @provider_class.clear + + @mount = mkmount + @target = @provider_class.default_target + end + + it "should write the mount to disk when :flush is called" do + old_text = @provider_class.target_object(@provider_class.default_target).read + + @mount.flush + + text = @provider_class.target_object(@provider_class.default_target).read + text.should == old_text + @mount.class.to_line(@mount.property_hash) + "\n" + end + end + + describe provider_class, " when parsing information about the root filesystem" do + confine "Mount type not tested on Darwin" => Facter["operatingsystem"].value != "Darwin" + include ParsedMountTesting + + before do + @mount = @mount_class.new :name => "/" + @provider = @mount.provider + end + + it "should have a filesystem tab" do + FileTest.should be_exist(@provider_class.default_target) + end + + it "should find the root filesystem" do + @provider_class.prefetch("/" => @mount) + @mount.provider.property_hash[:ensure].should == :present + end + + it "should determine that the root fs is mounted" do + @provider_class.prefetch("/" => @mount) + @mount.provider.should be_mounted + end + end + + describe provider_class, " when mounting and unmounting" do + include ParsedMountTesting + + it "should call the 'mount' command to mount the filesystem" + + it "should call the 'unmount' command to unmount the filesystem" + + it "should specify the filesystem when remounting a filesystem" + end +end diff --git a/spec/unit/provider/mount/parsed_spec_spec.rb b/spec/unit/provider/mount/parsed_spec_spec.rb deleted file mode 100755 index 0cf206d4b..000000000 --- a/spec/unit/provider/mount/parsed_spec_spec.rb +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-9-12. -# Copyright (c) 2006. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppettest/support/utils' -require 'puppettest/fileparsing' - -module ParsedMountTesting - include PuppetTest::Support::Utils - include PuppetTest::FileParsing - - def fake_fstab - os = Facter['operatingsystem'] - if os == "Solaris" - name = "solaris.fstab" - elsif os == "FreeBSD" - name = "freebsd.fstab" - else - # Catchall for other fstabs - name = "linux.fstab" - end - oldpath = @provider_class.default_target - return fakefile(File::join("data/types/mount", name)) - end - - def mkmountargs - mount = nil - - if defined? @pcount - @pcount += 1 - else - @pcount = 1 - end - args = { - :name => "/fspuppet%s" % @pcount, - :device => "/dev/dsk%s" % @pcount, - } - - @provider_class.fields(:parsed).each do |field| - unless args.include? field - args[field] = "fake%s%s" % [field, @pcount] - end - end - - return args - end - - def mkmount - hash = mkmountargs() - #hash[:provider] = @provider_class.name - - fakeresource = stub :type => :mount, :name => hash[:name] - fakeresource.stubs(:[]).with(:name).returns(hash[:name]) - fakeresource.stubs(:should).with(:target).returns(nil) - - mount = @provider_class.new(fakeresource) - hash[:record_type] = :parsed - hash[:ensure] = :present - mount.property_hash = hash - - return mount - end - - # Here we just create a fake host type that answers to all of the methods - # but does not modify our actual system. - def mkfaketype - @provider.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) - end -end - -provider_class = Puppet::Type.type(:mount).provider(:parsed) - -describe provider_class do - before :each do - @mount_class = Puppet::Type.type(:mount) - @provider_class = @mount_class.provider(:parsed) - end - - - describe provider_class do - include ParsedMountTesting - - it "should be able to parse all of the example mount tabs" do - tab = fake_fstab - @provider = @provider_class - - # LAK:FIXME Again, a relatively bad test, but I don't know how to rspec-ify this. - # I suppose this is more of an integration test? I dunno. - fakedataparse(tab) do - # Now just make we've got some mounts we know will be there - hashes = @provider_class.target_records(tab).find_all { |i| i.is_a? Hash } - (hashes.length > 0).should be_true - root = hashes.find { |i| i[:name] == "/" } - - proc { @provider_class.to_file(hashes) }.should_not raise_error - end - end - - # LAK:FIXME I can't mock Facter because this test happens at parse-time. - it "should default to /etc/vfstab on Solaris and /etc/fstab everywhere else" do - should = case Facter.value(:operatingsystem) - when "Solaris"; "/etc/vfstab" - else - "/etc/fstab" - end - Puppet::Type.type(:mount).provider(:parsed).default_target.should == should - end - - it "should not crash on incomplete lines in fstab" do - parse = @provider_class.parse <<-FSTAB -/dev/incomplete -/dev/device name - FSTAB - - lambda{ @provider_class.to_line(parse[0]) }.should_not raise_error - end - end - - describe provider_class, " when mounting an absent filesystem" do - include ParsedMountTesting - - # #730 - Make sure 'flush' is called when a mount is moving from absent to mounted - it "should flush the fstab to disk" do - mount = mkmount - - # Mark the mount as absent - mount.property_hash[:ensure] = :absent - - mount.stubs(:mountcmd) # just so we don't actually try to mount anything - - mount.expects(:flush) - mount.mount - end - end - - describe provider_class, " when modifying the filesystem tab" do - include ParsedMountTesting - before do - Puppet.settings.stubs(:use) - # Never write to disk, only to RAM. - #@provider_class.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) - @provider_class.stubs(:target_object).returns(Puppet::Util::FileType.filetype(:ram).new("eh")) - @provider_class.clear - - @mount = mkmount - @target = @provider_class.default_target - end - - it "should write the mount to disk when :flush is called" do - old_text = @provider_class.target_object(@provider_class.default_target).read - - @mount.flush - - text = @provider_class.target_object(@provider_class.default_target).read - text.should == old_text + @mount.class.to_line(@mount.property_hash) + "\n" - end - end - - describe provider_class, " when parsing information about the root filesystem" do - confine "Mount type not tested on Darwin" => Facter["operatingsystem"].value != "Darwin" - include ParsedMountTesting - - before do - @mount = @mount_class.new :name => "/" - @provider = @mount.provider - end - - it "should have a filesystem tab" do - FileTest.should be_exist(@provider_class.default_target) - end - - it "should find the root filesystem" do - @provider_class.prefetch("/" => @mount) - @mount.provider.property_hash[:ensure].should == :present - end - - it "should determine that the root fs is mounted" do - @provider_class.prefetch("/" => @mount) - @mount.provider.should be_mounted - end - end - - describe provider_class, " when mounting and unmounting" do - include ParsedMountTesting - - it "should call the 'mount' command to mount the filesystem" - - it "should call the 'unmount' command to unmount the filesystem" - - it "should specify the filesystem when remounting a filesystem" - end -end diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb new file mode 100755 index 000000000..41abcd424 --- /dev/null +++ b/spec/unit/provider/mount_spec.rb @@ -0,0 +1,130 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/mount' + +describe Puppet::Provider::Mount do + before :each do + @mounter = Object.new + @mounter.extend(Puppet::Provider::Mount) + + @name = "/" + + @resource = stub 'resource' + @resource.stubs(:[]).with(:name).returns(@name) + + @mounter.stubs(:resource).returns(@resource) + end + + describe Puppet::Provider::Mount, " when mounting" do + + it "should use the 'mountcmd' method to mount" do + @mounter.stubs(:options).returns(nil) + @mounter.expects(:mountcmd) + + @mounter.mount + end + + it "should flush before mounting if a flush method exists" do + @mounter.meta_def(:flush) { } + @mounter.expects(:flush) + @mounter.stubs(:mountcmd) + @mounter.stubs(:options).returns(nil) + + @mounter.mount + end + + it "should add the options following '-o' if they exist and are not set to :absent" do + @mounter.stubs(:options).returns("ro") + @mounter.expects(:mountcmd).with { |*ary| ary[0] == "-o" and ary[1] == "ro" } + + @mounter.mount + end + + it "should specify the filesystem name to the mount command" do + @mounter.stubs(:options).returns(nil) + @mounter.expects(:mountcmd).with { |*ary| ary[-1] == @name } + + @mounter.mount + end + end + + describe Puppet::Provider::Mount, " when remounting" do + + it "should use '-o remount' if the resource specifies it supports remounting" do + @mounter.stubs(:info) + @resource.stubs(:[]).with(:remounts).returns(:true) + @mounter.expects(:mountcmd).with("-o", "remount", @name) + @mounter.remount + end + + it "should unmount and mount if the resource does not specify it supports remounting" do + @mounter.stubs(:info) + @resource.stubs(:[]).with(:remounts).returns(false) + @mounter.expects(:unmount) + @mounter.expects(:mount) + @mounter.remount + end + + it "should log that it is remounting" do + @resource.stubs(:[]).with(:remounts).returns(:true) + @mounter.stubs(:mountcmd) + @mounter.expects(:info).with("Remounting") + @mounter.remount + end + end + + describe Puppet::Provider::Mount, " when unmounting" do + + it "should call the :umount command with the resource name" do + @mounter.expects(:umount).with(@name) + @mounter.unmount + end + end + + describe Puppet::Provider::Mount, " when determining if it is mounted" do + + it "should parse the results of running the mount command with no arguments" do + Facter.stubs(:value).returns("whatever") + @mounter.expects(:mountcmd).returns("") + + @mounter.mounted? + end + + it "should match ' on /private/var/automount' if the operating system is Darwin" do + Facter.stubs(:value).with("operatingsystem").returns("Darwin") + @mounter.expects(:mountcmd).returns("/dev/whatever on /private/var/automount/\ndevfs on /dev") + + @mounter.should be_mounted + end + + it "should match ' on ' if the operating system is Darwin" do + Facter.stubs(:value).with("operatingsystem").returns("Darwin") + @mounter.expects(:mountcmd).returns("/dev/disk03 on / (local, journaled)\ndevfs on /dev") + + @mounter.should be_mounted + end + + it "should match '^ on' if the operating system is Solaris" do + Facter.stubs(:value).with("operatingsystem").returns("Solaris") + @mounter.expects(:mountcmd).returns("/ on /dev/dsk/whatever\n/var on /dev/dsk/other") + + @mounter.should be_mounted + end + + it "should match ' on ' if the operating system is not Darwin or Solaris" do + Facter.stubs(:value).with("operatingsystem").returns("Debian") + @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on / and stuff\n/dev/other/disk on /var and stuff") + + @mounter.should be_mounted + end + + it "should not be considered mounted if it did not match the mount output" do + Facter.stubs(:value).with("operatingsystem").returns("Debian") + @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on /something/else and stuff\n/dev/other/disk on /var and stuff") + + @mounter.should_not be_mounted + end + end +end diff --git a/spec/unit/provider/mount_spec_spec.rb b/spec/unit/provider/mount_spec_spec.rb deleted file mode 100755 index 41abcd424..000000000 --- a/spec/unit/provider/mount_spec_spec.rb +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/provider/mount' - -describe Puppet::Provider::Mount do - before :each do - @mounter = Object.new - @mounter.extend(Puppet::Provider::Mount) - - @name = "/" - - @resource = stub 'resource' - @resource.stubs(:[]).with(:name).returns(@name) - - @mounter.stubs(:resource).returns(@resource) - end - - describe Puppet::Provider::Mount, " when mounting" do - - it "should use the 'mountcmd' method to mount" do - @mounter.stubs(:options).returns(nil) - @mounter.expects(:mountcmd) - - @mounter.mount - end - - it "should flush before mounting if a flush method exists" do - @mounter.meta_def(:flush) { } - @mounter.expects(:flush) - @mounter.stubs(:mountcmd) - @mounter.stubs(:options).returns(nil) - - @mounter.mount - end - - it "should add the options following '-o' if they exist and are not set to :absent" do - @mounter.stubs(:options).returns("ro") - @mounter.expects(:mountcmd).with { |*ary| ary[0] == "-o" and ary[1] == "ro" } - - @mounter.mount - end - - it "should specify the filesystem name to the mount command" do - @mounter.stubs(:options).returns(nil) - @mounter.expects(:mountcmd).with { |*ary| ary[-1] == @name } - - @mounter.mount - end - end - - describe Puppet::Provider::Mount, " when remounting" do - - it "should use '-o remount' if the resource specifies it supports remounting" do - @mounter.stubs(:info) - @resource.stubs(:[]).with(:remounts).returns(:true) - @mounter.expects(:mountcmd).with("-o", "remount", @name) - @mounter.remount - end - - it "should unmount and mount if the resource does not specify it supports remounting" do - @mounter.stubs(:info) - @resource.stubs(:[]).with(:remounts).returns(false) - @mounter.expects(:unmount) - @mounter.expects(:mount) - @mounter.remount - end - - it "should log that it is remounting" do - @resource.stubs(:[]).with(:remounts).returns(:true) - @mounter.stubs(:mountcmd) - @mounter.expects(:info).with("Remounting") - @mounter.remount - end - end - - describe Puppet::Provider::Mount, " when unmounting" do - - it "should call the :umount command with the resource name" do - @mounter.expects(:umount).with(@name) - @mounter.unmount - end - end - - describe Puppet::Provider::Mount, " when determining if it is mounted" do - - it "should parse the results of running the mount command with no arguments" do - Facter.stubs(:value).returns("whatever") - @mounter.expects(:mountcmd).returns("") - - @mounter.mounted? - end - - it "should match ' on /private/var/automount' if the operating system is Darwin" do - Facter.stubs(:value).with("operatingsystem").returns("Darwin") - @mounter.expects(:mountcmd).returns("/dev/whatever on /private/var/automount/\ndevfs on /dev") - - @mounter.should be_mounted - end - - it "should match ' on ' if the operating system is Darwin" do - Facter.stubs(:value).with("operatingsystem").returns("Darwin") - @mounter.expects(:mountcmd).returns("/dev/disk03 on / (local, journaled)\ndevfs on /dev") - - @mounter.should be_mounted - end - - it "should match '^ on' if the operating system is Solaris" do - Facter.stubs(:value).with("operatingsystem").returns("Solaris") - @mounter.expects(:mountcmd).returns("/ on /dev/dsk/whatever\n/var on /dev/dsk/other") - - @mounter.should be_mounted - end - - it "should match ' on ' if the operating system is not Darwin or Solaris" do - Facter.stubs(:value).with("operatingsystem").returns("Debian") - @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on / and stuff\n/dev/other/disk on /var and stuff") - - @mounter.should be_mounted - end - - it "should not be considered mounted if it did not match the mount output" do - Facter.stubs(:value).with("operatingsystem").returns("Debian") - @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on /something/else and stuff\n/dev/other/disk on /var and stuff") - - @mounter.should_not be_mounted - end - end -end diff --git a/spec/unit/provider/naginator_spec.rb b/spec/unit/provider/naginator_spec.rb new file mode 100755 index 000000000..d0d43aa4b --- /dev/null +++ b/spec/unit/provider/naginator_spec.rb @@ -0,0 +1,58 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/naginator' + +describe Puppet::Provider::Naginator do + before do + @resource_type = stub 'resource_type', :name => :nagios_test + @class = Class.new(Puppet::Provider::Naginator) + + @class.stubs(:resource_type).returns @resource_type + end + + it "should be able to look up the associated Nagios type" do + nagios_type = mock "nagios_type" + nagios_type.stubs :attr_accessor + Nagios::Base.expects(:type).with(:test).returns nagios_type + + @class.nagios_type.should equal(nagios_type) + end + + it "should use the Nagios type to determine whether an attribute is valid" do + nagios_type = mock "nagios_type" + nagios_type.stubs :attr_accessor + Nagios::Base.expects(:type).with(:test).returns nagios_type + + nagios_type.expects(:parameters).returns [:foo, :bar] + + @class.valid_attr?(:test, :foo).should be_true + end + + it "should use Naginator to parse configuration snippets" do + parser = mock 'parser' + parser.expects(:parse).with("my text").returns "my instances" + Nagios::Parser.expects(:new).returns(parser) + + @class.parse("my text").should == "my instances" + end + + it "should join Nagios::Base records with '\\n' when asked to convert them to text" do + @class.expects(:header).returns "myheader\n" + + @class.to_file([:one, :two]).should == "myheader\none\ntwo" + end + + it "should be able to prefetch instance from configuration files" do + @class.should respond_to(:prefetch) + end + + it "should be able to generate a list of instances" do + @class.should respond_to(:instances) + end + + it "should never skip records" do + @class.should_not be_skip_record("foo") + end +end diff --git a/spec/unit/provider/naginator_spec_spec.rb b/spec/unit/provider/naginator_spec_spec.rb deleted file mode 100755 index d0d43aa4b..000000000 --- a/spec/unit/provider/naginator_spec_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/provider/naginator' - -describe Puppet::Provider::Naginator do - before do - @resource_type = stub 'resource_type', :name => :nagios_test - @class = Class.new(Puppet::Provider::Naginator) - - @class.stubs(:resource_type).returns @resource_type - end - - it "should be able to look up the associated Nagios type" do - nagios_type = mock "nagios_type" - nagios_type.stubs :attr_accessor - Nagios::Base.expects(:type).with(:test).returns nagios_type - - @class.nagios_type.should equal(nagios_type) - end - - it "should use the Nagios type to determine whether an attribute is valid" do - nagios_type = mock "nagios_type" - nagios_type.stubs :attr_accessor - Nagios::Base.expects(:type).with(:test).returns nagios_type - - nagios_type.expects(:parameters).returns [:foo, :bar] - - @class.valid_attr?(:test, :foo).should be_true - end - - it "should use Naginator to parse configuration snippets" do - parser = mock 'parser' - parser.expects(:parse).with("my text").returns "my instances" - Nagios::Parser.expects(:new).returns(parser) - - @class.parse("my text").should == "my instances" - end - - it "should join Nagios::Base records with '\\n' when asked to convert them to text" do - @class.expects(:header).returns "myheader\n" - - @class.to_file([:one, :two]).should == "myheader\none\ntwo" - end - - it "should be able to prefetch instance from configuration files" do - @class.should respond_to(:prefetch) - end - - it "should be able to generate a list of instances" do - @class.should respond_to(:instances) - end - - it "should never skip records" do - @class.should_not be_skip_record("foo") - end -end diff --git a/spec/unit/provider/package/aix_spec.rb b/spec/unit/provider/package/aix_spec.rb new file mode 100755 index 000000000..691749df9 --- /dev/null +++ b/spec/unit/provider/package/aix_spec.rb @@ -0,0 +1,66 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:package).provider(:aix) + +describe provider_class do + before(:each) do + # Create a mock resource + @resource = stub 'resource' + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name and source + @resource.stubs(:[]).with(:name).returns "mypackage" + @resource.stubs(:[]).with(:source).returns "mysource" + @resource.stubs(:[]).with(:ensure).returns :installed + + @provider = provider_class.new + @provider.resource = @resource + end + + [:install, :uninstall, :latest, :query, :update].each do |method| + it "should have a #{method} method" do + @provider.should respond_to(method) + end + end + + it "should uninstall a package" do + @provider.expects(:installp).with('-gu', 'mypackage') + @provider.uninstall + end + + describe "when installing" do + it "should install a package" do + @resource.stubs(:should).with(:ensure).returns(:installed) + @provider.expects(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage') + @provider.install + end + + it "should install a specific package version" do + @resource.stubs(:should).with(:ensure).returns("1.2.3.4") + @provider.expects(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage 1.2.3.4') + @provider.install + end + end + + describe "when finding the latest version" do + it "should return the current version when no later version is present" do + @provider.stubs(:latest_info).returns(nil) + @provider.stubs(:properties).returns( { :ensure => "1.2.3.4" } ) + @provider.latest.should == "1.2.3.4" + end + + it "should return the latest version of a package" do + @provider.stubs(:latest_info).returns( { :version => "1.2.3.5" } ) + @provider.latest.should == "1.2.3.5" + end + end + + it "update should install a package" do + @provider.expects(:install).with(false) + @provider.update + end +end diff --git a/spec/unit/provider/package/aix_spec_spec.rb b/spec/unit/provider/package/aix_spec_spec.rb deleted file mode 100755 index 691749df9..000000000 --- a/spec/unit/provider/package/aix_spec_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:package).provider(:aix) - -describe provider_class do - before(:each) do - # Create a mock resource - @resource = stub 'resource' - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name and source - @resource.stubs(:[]).with(:name).returns "mypackage" - @resource.stubs(:[]).with(:source).returns "mysource" - @resource.stubs(:[]).with(:ensure).returns :installed - - @provider = provider_class.new - @provider.resource = @resource - end - - [:install, :uninstall, :latest, :query, :update].each do |method| - it "should have a #{method} method" do - @provider.should respond_to(method) - end - end - - it "should uninstall a package" do - @provider.expects(:installp).with('-gu', 'mypackage') - @provider.uninstall - end - - describe "when installing" do - it "should install a package" do - @resource.stubs(:should).with(:ensure).returns(:installed) - @provider.expects(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage') - @provider.install - end - - it "should install a specific package version" do - @resource.stubs(:should).with(:ensure).returns("1.2.3.4") - @provider.expects(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage 1.2.3.4') - @provider.install - end - end - - describe "when finding the latest version" do - it "should return the current version when no later version is present" do - @provider.stubs(:latest_info).returns(nil) - @provider.stubs(:properties).returns( { :ensure => "1.2.3.4" } ) - @provider.latest.should == "1.2.3.4" - end - - it "should return the latest version of a package" do - @provider.stubs(:latest_info).returns( { :version => "1.2.3.5" } ) - @provider.latest.should == "1.2.3.5" - end - end - - it "update should install a package" do - @provider.expects(:install).with(false) - @provider.update - end -end diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb new file mode 100755 index 000000000..861029838 --- /dev/null +++ b/spec/unit/provider/package/apt_spec.rb @@ -0,0 +1,145 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider = Puppet::Type.type(:package).provider(:apt) + +describe provider do + before do + @resource = stub 'resource', :[] => "asdf" + @provider = provider.new(@resource) + + @fakeresult = "install ok installed asdf 1.0\n" + end + + it "should be versionable" do + provider.should be_versionable + end + + it "should use :install to update" do + @provider.expects(:install) + @provider.update + end + + it "should use 'apt-get remove' to uninstall" do + @provider.expects(:aptget).with("-y", "-q", :remove, "asdf") + + @provider.uninstall + end + + it "should use 'apt-get purge' and 'dpkg purge' to purge" do + @provider.expects(:aptget).with("-y", "-q", :remove, "--purge", "asdf") + @provider.expects(:dpkg).with("--purge", "asdf") + + @provider.purge + end + + it "should use 'apt-cache policy' to determine the latest version of a package" do + @provider.expects(:aptcache).with(:policy, "asdf").returns "asdf: +Installed: 1:1.0 +Candidate: 1:1.1 +Version table: + 1:1.0 + 650 http://ftp.osuosl.org testing/main Packages +*** 1:1.1 + 100 /var/lib/dpkg/status" + + @provider.latest.should == "1:1.1" + end + + it "should print and error and return nil if no policy is found" do + @provider.expects(:aptcache).with(:policy, "asdf").returns "asdf:" + + @provider.expects(:err) + @provider.latest.should be_nil + end + + it "should be able to preseed" do + @provider.should respond_to(:run_preseed) + end + + it "should preseed with the provided responsefile when preseeding is called for" do + @resource.expects(:[]).with(:responsefile).returns "/my/file" + FileTest.expects(:exist?).with("/my/file").returns true + + @provider.expects(:info) + @provider.expects(:preseed).with("/my/file") + + @provider.run_preseed + end + + it "should not preseed if no responsefile is provided" do + @resource.expects(:[]).with(:responsefile).returns nil + + @provider.expects(:info) + @provider.expects(:preseed).never + + @provider.run_preseed + end + + it "should fail if a cdrom is listed in the sources list and :allowcdrom is not specified" + + describe "when installing" do + it "should preseed if a responsefile is provided" do + @resource.expects(:[]).with(:responsefile).returns "/my/file" + @provider.expects(:run_preseed) + + @provider.stubs(:aptget) + @provider.install + end + + it "should check for a cdrom" do + @provider.expects(:checkforcdrom) + + @provider.stubs(:aptget) + @provider.install + end + + it "should use 'apt-get install' with the package name if no version is asked for" do + @resource.expects(:[]).with(:ensure).returns :installed + @provider.expects(:aptget).with { |*command| command[-1] == "asdf" and command[-2] == :install } + + @provider.install + end + + it "should specify the package version if one is asked for" do + @resource.expects(:[]).with(:ensure).returns "1.0" + @provider.expects(:aptget).with { |*command| command[-1] == "asdf=1.0" } + + @provider.install + end + + it "should use --force-yes if a package version is specified" do + @resource.expects(:[]).with(:ensure).returns "1.0" + @provider.expects(:aptget).with { |*command| command.include?("--force-yes") } + + @provider.install + end + + it "should do a quiet install" do + @provider.expects(:aptget).with { |*command| command.include?("-q") } + + @provider.install + end + + it "should default to 'yes' for all questions" do + @provider.expects(:aptget).with { |*command| command.include?("-y") } + + @provider.install + end + + it "should keep config files if asked" do + @resource.expects(:[]).with(:configfiles).returns :keep + @provider.expects(:aptget).with { |*command| command.include?("DPkg::Options::=--force-confold") } + + @provider.install + end + + it "should replace config files if asked" do + @resource.expects(:[]).with(:configfiles).returns :replace + @provider.expects(:aptget).with { |*command| command.include?("DPkg::Options::=--force-confnew") } + + @provider.install + end + end +end diff --git a/spec/unit/provider/package/apt_spec_spec.rb b/spec/unit/provider/package/apt_spec_spec.rb deleted file mode 100755 index 861029838..000000000 --- a/spec/unit/provider/package/apt_spec_spec.rb +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -provider = Puppet::Type.type(:package).provider(:apt) - -describe provider do - before do - @resource = stub 'resource', :[] => "asdf" - @provider = provider.new(@resource) - - @fakeresult = "install ok installed asdf 1.0\n" - end - - it "should be versionable" do - provider.should be_versionable - end - - it "should use :install to update" do - @provider.expects(:install) - @provider.update - end - - it "should use 'apt-get remove' to uninstall" do - @provider.expects(:aptget).with("-y", "-q", :remove, "asdf") - - @provider.uninstall - end - - it "should use 'apt-get purge' and 'dpkg purge' to purge" do - @provider.expects(:aptget).with("-y", "-q", :remove, "--purge", "asdf") - @provider.expects(:dpkg).with("--purge", "asdf") - - @provider.purge - end - - it "should use 'apt-cache policy' to determine the latest version of a package" do - @provider.expects(:aptcache).with(:policy, "asdf").returns "asdf: -Installed: 1:1.0 -Candidate: 1:1.1 -Version table: - 1:1.0 - 650 http://ftp.osuosl.org testing/main Packages -*** 1:1.1 - 100 /var/lib/dpkg/status" - - @provider.latest.should == "1:1.1" - end - - it "should print and error and return nil if no policy is found" do - @provider.expects(:aptcache).with(:policy, "asdf").returns "asdf:" - - @provider.expects(:err) - @provider.latest.should be_nil - end - - it "should be able to preseed" do - @provider.should respond_to(:run_preseed) - end - - it "should preseed with the provided responsefile when preseeding is called for" do - @resource.expects(:[]).with(:responsefile).returns "/my/file" - FileTest.expects(:exist?).with("/my/file").returns true - - @provider.expects(:info) - @provider.expects(:preseed).with("/my/file") - - @provider.run_preseed - end - - it "should not preseed if no responsefile is provided" do - @resource.expects(:[]).with(:responsefile).returns nil - - @provider.expects(:info) - @provider.expects(:preseed).never - - @provider.run_preseed - end - - it "should fail if a cdrom is listed in the sources list and :allowcdrom is not specified" - - describe "when installing" do - it "should preseed if a responsefile is provided" do - @resource.expects(:[]).with(:responsefile).returns "/my/file" - @provider.expects(:run_preseed) - - @provider.stubs(:aptget) - @provider.install - end - - it "should check for a cdrom" do - @provider.expects(:checkforcdrom) - - @provider.stubs(:aptget) - @provider.install - end - - it "should use 'apt-get install' with the package name if no version is asked for" do - @resource.expects(:[]).with(:ensure).returns :installed - @provider.expects(:aptget).with { |*command| command[-1] == "asdf" and command[-2] == :install } - - @provider.install - end - - it "should specify the package version if one is asked for" do - @resource.expects(:[]).with(:ensure).returns "1.0" - @provider.expects(:aptget).with { |*command| command[-1] == "asdf=1.0" } - - @provider.install - end - - it "should use --force-yes if a package version is specified" do - @resource.expects(:[]).with(:ensure).returns "1.0" - @provider.expects(:aptget).with { |*command| command.include?("--force-yes") } - - @provider.install - end - - it "should do a quiet install" do - @provider.expects(:aptget).with { |*command| command.include?("-q") } - - @provider.install - end - - it "should default to 'yes' for all questions" do - @provider.expects(:aptget).with { |*command| command.include?("-y") } - - @provider.install - end - - it "should keep config files if asked" do - @resource.expects(:[]).with(:configfiles).returns :keep - @provider.expects(:aptget).with { |*command| command.include?("DPkg::Options::=--force-confold") } - - @provider.install - end - - it "should replace config files if asked" do - @resource.expects(:[]).with(:configfiles).returns :replace - @provider.expects(:aptget).with { |*command| command.include?("DPkg::Options::=--force-confnew") } - - @provider.install - end - end -end diff --git a/spec/unit/provider/package/dpkg_spec.rb b/spec/unit/provider/package/dpkg_spec.rb new file mode 100755 index 000000000..d0c1d2622 --- /dev/null +++ b/spec/unit/provider/package/dpkg_spec.rb @@ -0,0 +1,225 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider = Puppet::Type.type(:package).provider(:dpkg) + +describe provider do + before do + @resource = stub 'resource', :[] => "asdf" + @provider = provider.new(@resource) + @provider.expects(:execute).never # forbid "manual" executions + + @fakeresult = "install ok installed asdf 1.0\n" + end + + it "should have documentation" do + provider.doc.should be_instance_of(String) + end + + describe "when listing all instances" do + before do + provider.stubs(:command).with(:dpkgquery).returns "myquery" + end + + it "should use dpkg-query" do + provider.expects(:command).with(:dpkgquery).returns "myquery" + provider.expects(:execpipe).with("myquery -W --showformat '${Status} ${Package} ${Version}\\n'").returns @fakeresult + + provider.instances + end + + it "should create and return an instance with each parsed line from dpkg-query" do + pipe = mock 'pipe' + pipe.expects(:each).yields @fakeresult + provider.expects(:execpipe).yields pipe + + asdf = mock 'pkg1' + provider.expects(:new).with(:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg).returns asdf + + provider.instances.should == [asdf] + end + + it "should warn on and ignore any lines it does not understand" do + pipe = mock 'pipe' + pipe.expects(:each).yields "foobar" + provider.expects(:execpipe).yields pipe + + Puppet.expects(:warning) + provider.expects(:new).never + + provider.instances.should == [] + end + end + + describe "when querying the current state" do + it "should use dpkg-query" do + @provider.expects(:dpkgquery).with("-W", "--showformat",'${Status} ${Package} ${Version}\\n', "asdf").returns @fakeresult + + @provider.query + end + + it "should consider the package purged if dpkg-query fails" do + @provider.expects(:dpkgquery).raises Puppet::ExecutionFailure.new("eh") + + @provider.query[:ensure].should == :purged + end + + it "should return a hash of the found status with the desired state, error state, status, name, and 'ensure'" do + @provider.expects(:dpkgquery).returns @fakeresult + + @provider.query.should == {:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg} + end + + it "should consider the package absent if the dpkg-query result cannot be interpreted" do + @provider.expects(:dpkgquery).returns "somebaddata" + + @provider.query[:ensure].should == :absent + end + + it "should fail if an error is discovered" do + @provider.expects(:dpkgquery).returns @fakeresult.sub("ok", "error") + + lambda { @provider.query }.should raise_error(Puppet::Error) + end + + it "should consider the package purged if it is marked 'not-installed'" do + @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "not-installed") + + @provider.query[:ensure].should == :purged + end + + it "should consider the package absent if it is marked 'config-files'" do + @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "config-files") + @provider.query[:ensure].should == :absent + end + + it "should consider the package absent if it is marked 'half-installed'" do + @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "half-installed") + @provider.query[:ensure].should == :absent + end + + it "should consider the package absent if it is marked 'unpacked'" do + @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "unpacked") + @provider.query[:ensure].should == :absent + end + + it "should consider the package absent if it is marked 'half-configured'" do + @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "half-configured") + @provider.query[:ensure].should == :absent + end + + it "should consider the package held if its state is 'hold'" do + @provider.expects(:dpkgquery).returns @fakeresult.sub("install", "hold") + @provider.query[:ensure].should == :held + end + end + + it "should be able to install" do + @provider.should respond_to(:install) + end + + describe "when installing" do + before do + @resource.stubs(:[]).with(:source).returns "mypkg" + end + + it "should fail to install if no source is specified in the resource" do + @resource.expects(:[]).with(:source).returns nil + + lambda { @provider.install }.should raise_error(ArgumentError) + end + + it "should use 'dpkg -i' to install the package" do + @resource.expects(:[]).with(:source).returns "mypackagefile" + @provider.expects(:unhold) + @provider.expects(:dpkg).with { |*command| command[-1] == "mypackagefile" and command[-2] == "-i" } + + @provider.install + end + + it "should keep old config files if told to do so" do + @resource.expects(:[]).with(:configfiles).returns :keep + @provider.expects(:unhold) + @provider.expects(:dpkg).with { |*command| command[0] == "--force-confold" } + + @provider.install + end + + it "should replace old config files if told to do so" do + @resource.expects(:[]).with(:configfiles).returns :replace + @provider.expects(:unhold) + @provider.expects(:dpkg).with { |*command| command[0] == "--force-confnew" } + + @provider.install + end + + it "should ensure any hold is removed" do + @provider.expects(:unhold).once + @provider.expects(:dpkg) + @provider.install + end + end + + describe "when holding or unholding" do + before do + @tempfile = stub 'tempfile', :print => nil, :close => nil, :flush => nil, :path => "/other/file" + @tempfile.stubs(:write) + Tempfile.stubs(:new).returns @tempfile + end + + it "should install first if holding" do + @provider.stubs(:execute) + @provider.expects(:install).once + @provider.hold + end + + it "should execute dpkg --set-selections when holding" do + @provider.stubs(:install) + @provider.expects(:execute).with([:dpkg, '--set-selections'], {:stdinfile => @tempfile.path}).once + @provider.hold + end + + it "should execute dpkg --set-selections when unholding" do + @provider.stubs(:install) + @provider.expects(:execute).with([:dpkg, '--set-selections'], {:stdinfile => @tempfile.path}).once + @provider.hold + end + end + + it "should use :install to update" do + @provider.expects(:install) + @provider.update + end + + describe "when determining latest available version" do + it "should return the version found by dpkg-deb" do + @resource.expects(:[]).with(:source).returns "myfile" + @provider.expects(:dpkg_deb).with { |*command| command[-1] == "myfile" }.returns "asdf\t1.0" + @provider.latest.should == "1.0" + end + + it "should warn if the package file contains a different package" do + @provider.expects(:dpkg_deb).returns("foo\tversion") + @provider.expects(:warning) + @provider.latest + end + + it "should cope with names containing ++" do + @resource = stub 'resource', :[] => "asdf++" + @provider = provider.new(@resource) + @provider.expects(:dpkg_deb).returns "asdf++\t1.0" + @provider.latest.should == "1.0" + end + end + + it "should use 'dpkg -r' to uninstall" do + @provider.expects(:dpkg).with("-r", "asdf") + @provider.uninstall + end + + it "should use 'dpkg --purge' to purge" do + @provider.expects(:dpkg).with("--purge", "asdf") + @provider.purge + end +end diff --git a/spec/unit/provider/package/dpkg_spec_spec.rb b/spec/unit/provider/package/dpkg_spec_spec.rb deleted file mode 100755 index d0c1d2622..000000000 --- a/spec/unit/provider/package/dpkg_spec_spec.rb +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -provider = Puppet::Type.type(:package).provider(:dpkg) - -describe provider do - before do - @resource = stub 'resource', :[] => "asdf" - @provider = provider.new(@resource) - @provider.expects(:execute).never # forbid "manual" executions - - @fakeresult = "install ok installed asdf 1.0\n" - end - - it "should have documentation" do - provider.doc.should be_instance_of(String) - end - - describe "when listing all instances" do - before do - provider.stubs(:command).with(:dpkgquery).returns "myquery" - end - - it "should use dpkg-query" do - provider.expects(:command).with(:dpkgquery).returns "myquery" - provider.expects(:execpipe).with("myquery -W --showformat '${Status} ${Package} ${Version}\\n'").returns @fakeresult - - provider.instances - end - - it "should create and return an instance with each parsed line from dpkg-query" do - pipe = mock 'pipe' - pipe.expects(:each).yields @fakeresult - provider.expects(:execpipe).yields pipe - - asdf = mock 'pkg1' - provider.expects(:new).with(:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg).returns asdf - - provider.instances.should == [asdf] - end - - it "should warn on and ignore any lines it does not understand" do - pipe = mock 'pipe' - pipe.expects(:each).yields "foobar" - provider.expects(:execpipe).yields pipe - - Puppet.expects(:warning) - provider.expects(:new).never - - provider.instances.should == [] - end - end - - describe "when querying the current state" do - it "should use dpkg-query" do - @provider.expects(:dpkgquery).with("-W", "--showformat",'${Status} ${Package} ${Version}\\n', "asdf").returns @fakeresult - - @provider.query - end - - it "should consider the package purged if dpkg-query fails" do - @provider.expects(:dpkgquery).raises Puppet::ExecutionFailure.new("eh") - - @provider.query[:ensure].should == :purged - end - - it "should return a hash of the found status with the desired state, error state, status, name, and 'ensure'" do - @provider.expects(:dpkgquery).returns @fakeresult - - @provider.query.should == {:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg} - end - - it "should consider the package absent if the dpkg-query result cannot be interpreted" do - @provider.expects(:dpkgquery).returns "somebaddata" - - @provider.query[:ensure].should == :absent - end - - it "should fail if an error is discovered" do - @provider.expects(:dpkgquery).returns @fakeresult.sub("ok", "error") - - lambda { @provider.query }.should raise_error(Puppet::Error) - end - - it "should consider the package purged if it is marked 'not-installed'" do - @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "not-installed") - - @provider.query[:ensure].should == :purged - end - - it "should consider the package absent if it is marked 'config-files'" do - @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "config-files") - @provider.query[:ensure].should == :absent - end - - it "should consider the package absent if it is marked 'half-installed'" do - @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "half-installed") - @provider.query[:ensure].should == :absent - end - - it "should consider the package absent if it is marked 'unpacked'" do - @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "unpacked") - @provider.query[:ensure].should == :absent - end - - it "should consider the package absent if it is marked 'half-configured'" do - @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "half-configured") - @provider.query[:ensure].should == :absent - end - - it "should consider the package held if its state is 'hold'" do - @provider.expects(:dpkgquery).returns @fakeresult.sub("install", "hold") - @provider.query[:ensure].should == :held - end - end - - it "should be able to install" do - @provider.should respond_to(:install) - end - - describe "when installing" do - before do - @resource.stubs(:[]).with(:source).returns "mypkg" - end - - it "should fail to install if no source is specified in the resource" do - @resource.expects(:[]).with(:source).returns nil - - lambda { @provider.install }.should raise_error(ArgumentError) - end - - it "should use 'dpkg -i' to install the package" do - @resource.expects(:[]).with(:source).returns "mypackagefile" - @provider.expects(:unhold) - @provider.expects(:dpkg).with { |*command| command[-1] == "mypackagefile" and command[-2] == "-i" } - - @provider.install - end - - it "should keep old config files if told to do so" do - @resource.expects(:[]).with(:configfiles).returns :keep - @provider.expects(:unhold) - @provider.expects(:dpkg).with { |*command| command[0] == "--force-confold" } - - @provider.install - end - - it "should replace old config files if told to do so" do - @resource.expects(:[]).with(:configfiles).returns :replace - @provider.expects(:unhold) - @provider.expects(:dpkg).with { |*command| command[0] == "--force-confnew" } - - @provider.install - end - - it "should ensure any hold is removed" do - @provider.expects(:unhold).once - @provider.expects(:dpkg) - @provider.install - end - end - - describe "when holding or unholding" do - before do - @tempfile = stub 'tempfile', :print => nil, :close => nil, :flush => nil, :path => "/other/file" - @tempfile.stubs(:write) - Tempfile.stubs(:new).returns @tempfile - end - - it "should install first if holding" do - @provider.stubs(:execute) - @provider.expects(:install).once - @provider.hold - end - - it "should execute dpkg --set-selections when holding" do - @provider.stubs(:install) - @provider.expects(:execute).with([:dpkg, '--set-selections'], {:stdinfile => @tempfile.path}).once - @provider.hold - end - - it "should execute dpkg --set-selections when unholding" do - @provider.stubs(:install) - @provider.expects(:execute).with([:dpkg, '--set-selections'], {:stdinfile => @tempfile.path}).once - @provider.hold - end - end - - it "should use :install to update" do - @provider.expects(:install) - @provider.update - end - - describe "when determining latest available version" do - it "should return the version found by dpkg-deb" do - @resource.expects(:[]).with(:source).returns "myfile" - @provider.expects(:dpkg_deb).with { |*command| command[-1] == "myfile" }.returns "asdf\t1.0" - @provider.latest.should == "1.0" - end - - it "should warn if the package file contains a different package" do - @provider.expects(:dpkg_deb).returns("foo\tversion") - @provider.expects(:warning) - @provider.latest - end - - it "should cope with names containing ++" do - @resource = stub 'resource', :[] => "asdf++" - @provider = provider.new(@resource) - @provider.expects(:dpkg_deb).returns "asdf++\t1.0" - @provider.latest.should == "1.0" - end - end - - it "should use 'dpkg -r' to uninstall" do - @provider.expects(:dpkg).with("-r", "asdf") - @provider.uninstall - end - - it "should use 'dpkg --purge' to purge" do - @provider.expects(:dpkg).with("--purge", "asdf") - @provider.purge - end -end diff --git a/spec/unit/provider/package/gem_spec.rb b/spec/unit/provider/package/gem_spec.rb new file mode 100644 index 000000000..3dc1fa347 --- /dev/null +++ b/spec/unit/provider/package/gem_spec.rb @@ -0,0 +1,87 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:package).provider(:gem) + +describe provider_class do + it "should have an install method" do + @provider = provider_class.new + @provider.should respond_to(:install) + end + + describe "when installing" do + before do + # Create a mock resource + @resource = stub 'resource' + + # A catch all; no parameters set + @resource.stubs(:[]).returns nil + + # We have to set a name, though + @resource.stubs(:[]).with(:name).returns "myresource" + @resource.stubs(:[]).with(:ensure).returns :installed + + @provider = provider_class.new + @provider.stubs(:resource).returns @resource + end + + it "should use the path to the gem" do + provider_class.stubs(:command).with(:gemcmd).returns "/my/gem" + @provider.expects(:execute).with { |args| args[0] == "/my/gem" }.returns "" + @provider.install + end + + it "should specify that the gem is being installed" do + @provider.expects(:execute).with { |args| args[1] == "install" }.returns "" + @provider.install + end + + it "should specify that dependencies should be included" do + @provider.expects(:execute).with { |args| args[2] == "--include-dependencies" }.returns "" + @provider.install + end + + it "should specify the package name" do + @provider.expects(:execute).with { |args| args[3] == "myresource" }.returns "" + @provider.install + end + + describe "when a source is specified" do + describe "as a normal file" do + it "should use the file name instead of the gem name" do + @resource.stubs(:[]).with(:source).returns "/my/file" + @provider.expects(:execute).with { |args| args[3] == "/my/file" }.returns "" + @provider.install + end + end + describe "as a file url" do + it "should use the file name instead of the gem name" do + @resource.stubs(:[]).with(:source).returns "file:///my/file" + @provider.expects(:execute).with { |args| args[3] == "/my/file" }.returns "" + @provider.install + end + end + describe "as a puppet url" do + it "should fail" do + @resource.stubs(:[]).with(:source).returns "puppet://my/file" + lambda { @provider.install }.should raise_error(Puppet::Error) + end + end + describe "as a non-file and non-puppet url" do + it "should treat the source as a gem repository" do + @resource.stubs(:[]).with(:source).returns "http://host/my/file" + @provider.expects(:execute).with { |args| args[3..5] == ["--source", "http://host/my/file", "myresource"] }.returns "" + @provider.install + end + end + describe "with an invalid uri" do + it "should fail" do + URI.expects(:parse).raises(ArgumentError) + @resource.stubs(:[]).with(:source).returns "http:::::uppet:/:/my/file" + lambda { @provider.install }.should raise_error(Puppet::Error) + end + end + end + end +end diff --git a/spec/unit/provider/package/gem_spec_spec.rb b/spec/unit/provider/package/gem_spec_spec.rb deleted file mode 100644 index 3dc1fa347..000000000 --- a/spec/unit/provider/package/gem_spec_spec.rb +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:package).provider(:gem) - -describe provider_class do - it "should have an install method" do - @provider = provider_class.new - @provider.should respond_to(:install) - end - - describe "when installing" do - before do - # Create a mock resource - @resource = stub 'resource' - - # A catch all; no parameters set - @resource.stubs(:[]).returns nil - - # We have to set a name, though - @resource.stubs(:[]).with(:name).returns "myresource" - @resource.stubs(:[]).with(:ensure).returns :installed - - @provider = provider_class.new - @provider.stubs(:resource).returns @resource - end - - it "should use the path to the gem" do - provider_class.stubs(:command).with(:gemcmd).returns "/my/gem" - @provider.expects(:execute).with { |args| args[0] == "/my/gem" }.returns "" - @provider.install - end - - it "should specify that the gem is being installed" do - @provider.expects(:execute).with { |args| args[1] == "install" }.returns "" - @provider.install - end - - it "should specify that dependencies should be included" do - @provider.expects(:execute).with { |args| args[2] == "--include-dependencies" }.returns "" - @provider.install - end - - it "should specify the package name" do - @provider.expects(:execute).with { |args| args[3] == "myresource" }.returns "" - @provider.install - end - - describe "when a source is specified" do - describe "as a normal file" do - it "should use the file name instead of the gem name" do - @resource.stubs(:[]).with(:source).returns "/my/file" - @provider.expects(:execute).with { |args| args[3] == "/my/file" }.returns "" - @provider.install - end - end - describe "as a file url" do - it "should use the file name instead of the gem name" do - @resource.stubs(:[]).with(:source).returns "file:///my/file" - @provider.expects(:execute).with { |args| args[3] == "/my/file" }.returns "" - @provider.install - end - end - describe "as a puppet url" do - it "should fail" do - @resource.stubs(:[]).with(:source).returns "puppet://my/file" - lambda { @provider.install }.should raise_error(Puppet::Error) - end - end - describe "as a non-file and non-puppet url" do - it "should treat the source as a gem repository" do - @resource.stubs(:[]).with(:source).returns "http://host/my/file" - @provider.expects(:execute).with { |args| args[3..5] == ["--source", "http://host/my/file", "myresource"] }.returns "" - @provider.install - end - end - describe "with an invalid uri" do - it "should fail" do - URI.expects(:parse).raises(ArgumentError) - @resource.stubs(:[]).with(:source).returns "http:::::uppet:/:/my/file" - lambda { @provider.install }.should raise_error(Puppet::Error) - end - end - end - end -end diff --git a/spec/unit/provider/package/hpux_spec.rb b/spec/unit/provider/package/hpux_spec.rb new file mode 100644 index 000000000..32eae5189 --- /dev/null +++ b/spec/unit/provider/package/hpux_spec.rb @@ -0,0 +1,52 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:package).provider(:hpux) + +describe provider_class do + before(:each) do + # Create a mock resource + @resource = stub 'resource' + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name and source + @resource.stubs(:[]).with(:name).returns "mypackage" + @resource.stubs(:[]).with(:source).returns "mysource" + @resource.stubs(:[]).with(:ensure).returns :installed + + @provider = provider_class.new + @provider.stubs(:resource).returns @resource + end + + it "should have an install method" do + @provider = provider_class.new + @provider.should respond_to(:install) + end + + it "should have an uninstall method" do + @provider = provider_class.new + @provider.should respond_to(:uninstall) + end + + it "should have a swlist method" do + @provider = provider_class.new + @provider.should respond_to(:swlist) + end + + describe "when installing" do + it "should use a command-line like 'swinstall -x mount_all_filesystems=false -s SOURCE PACKAGE-NAME'" do + @provider.expects(:swinstall).with('-x', 'mount_all_filesystems=false', '-s', 'mysource', 'mypackage') + @provider.install + end + end + + describe "when uninstalling" do + it "should use a command-line like 'swremove -x mount_all_filesystems=false PACKAGE-NAME'" do + @provider.expects(:swremove).with('-x', 'mount_all_filesystems=false', 'mypackage') + @provider.uninstall + end + end + end diff --git a/spec/unit/provider/package/hpux_spec_spec.rb b/spec/unit/provider/package/hpux_spec_spec.rb deleted file mode 100644 index 32eae5189..000000000 --- a/spec/unit/provider/package/hpux_spec_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:package).provider(:hpux) - -describe provider_class do - before(:each) do - # Create a mock resource - @resource = stub 'resource' - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name and source - @resource.stubs(:[]).with(:name).returns "mypackage" - @resource.stubs(:[]).with(:source).returns "mysource" - @resource.stubs(:[]).with(:ensure).returns :installed - - @provider = provider_class.new - @provider.stubs(:resource).returns @resource - end - - it "should have an install method" do - @provider = provider_class.new - @provider.should respond_to(:install) - end - - it "should have an uninstall method" do - @provider = provider_class.new - @provider.should respond_to(:uninstall) - end - - it "should have a swlist method" do - @provider = provider_class.new - @provider.should respond_to(:swlist) - end - - describe "when installing" do - it "should use a command-line like 'swinstall -x mount_all_filesystems=false -s SOURCE PACKAGE-NAME'" do - @provider.expects(:swinstall).with('-x', 'mount_all_filesystems=false', '-s', 'mysource', 'mypackage') - @provider.install - end - end - - describe "when uninstalling" do - it "should use a command-line like 'swremove -x mount_all_filesystems=false PACKAGE-NAME'" do - @provider.expects(:swremove).with('-x', 'mount_all_filesystems=false', 'mypackage') - @provider.uninstall - end - end - end diff --git a/spec/unit/provider/package/nim_spec.rb b/spec/unit/provider/package/nim_spec.rb new file mode 100755 index 000000000..2018f6506 --- /dev/null +++ b/spec/unit/provider/package/nim_spec.rb @@ -0,0 +1,42 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:package).provider(:nim) + +describe provider_class do + before(:each) do + # Create a mock resource + @resource = stub 'resource' + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name and source + @resource.stubs(:[]).with(:name).returns "mypackage" + @resource.stubs(:[]).with(:source).returns "mysource" + @resource.stubs(:[]).with(:ensure).returns :installed + + @provider = provider_class.new + @provider.resource = @resource + end + + it "should have an install method" do + @provider = provider_class.new + @provider.should respond_to(:install) + end + + describe "when installing" do + it "should install a package" do + @resource.stubs(:should).with(:ensure).returns(:installed) + @provider.expects(:nimclient).with("-o", "cust", "-a", "installp_flags=acgwXY", "-a", "lpp_source=mysource", "-a", "filesets='mypackage'") + @provider.install + end + + it "should install a versioned package" do + @resource.stubs(:should).with(:ensure).returns("1.2.3.4") + @provider.expects(:nimclient).with("-o", "cust", "-a", "installp_flags=acgwXY", "-a", "lpp_source=mysource", "-a", "filesets='mypackage 1.2.3.4'") + @provider.install + end + end + end diff --git a/spec/unit/provider/package/nim_spec_spec.rb b/spec/unit/provider/package/nim_spec_spec.rb deleted file mode 100755 index 2018f6506..000000000 --- a/spec/unit/provider/package/nim_spec_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:package).provider(:nim) - -describe provider_class do - before(:each) do - # Create a mock resource - @resource = stub 'resource' - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name and source - @resource.stubs(:[]).with(:name).returns "mypackage" - @resource.stubs(:[]).with(:source).returns "mysource" - @resource.stubs(:[]).with(:ensure).returns :installed - - @provider = provider_class.new - @provider.resource = @resource - end - - it "should have an install method" do - @provider = provider_class.new - @provider.should respond_to(:install) - end - - describe "when installing" do - it "should install a package" do - @resource.stubs(:should).with(:ensure).returns(:installed) - @provider.expects(:nimclient).with("-o", "cust", "-a", "installp_flags=acgwXY", "-a", "lpp_source=mysource", "-a", "filesets='mypackage'") - @provider.install - end - - it "should install a versioned package" do - @resource.stubs(:should).with(:ensure).returns("1.2.3.4") - @provider.expects(:nimclient).with("-o", "cust", "-a", "installp_flags=acgwXY", "-a", "lpp_source=mysource", "-a", "filesets='mypackage 1.2.3.4'") - @provider.install - end - end - end diff --git a/spec/unit/provider/package/pkg_spec.rb b/spec/unit/provider/package/pkg_spec.rb new file mode 100644 index 000000000..d0d933d5e --- /dev/null +++ b/spec/unit/provider/package/pkg_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider = Puppet::Type.type(:package).provider(:pkg) + +describe provider do + before do + @resource = stub 'resource', :[] => "dummy" + @provider = provider.new(@resource) + + @fakeresult = "install ok installed dummy 1.0\n" + end + + def self.it_should_respond_to(*actions) + actions.each do |action| + it "should respond to :#{action}" do + @provider.should respond_to(action) + end + end + end + + it_should_respond_to :install, :uninstall, :update, :query, :latest + + it "should be versionable" do + provider.should_not be_versionable + end + + it "should use :install to update" do + @provider.expects(:install) + @provider.update + end + + it "should parse a line correctly" do + result = provider.parse_line("dummy 1.0@1.0-1.0 installed ----") + result.should == {:name => "dummy", :version => "1.0@1.0-1.0", + :ensure => :present, :status => "installed", + :provider => :pkg, :error => "ok"} + end + + it "should fail to parse an incorrect line" do + result = provider.parse_line("foo") + result.should be_nil + end + + it "should fail to list a missing package" do + @provider.expects(:pkg).with(:list, "-H", "dummy").returns "1" + @provider.query.should == {:status=>"missing", :ensure=>:absent, + :name=>"dummy", :error=>"ok"} + end + + it "should fail to list a package when it can't parse the output line" do + @provider.expects(:pkg).with(:list, "-H", "dummy").returns "failed" + @provider.query.should == {:status=>"missing", :ensure=>:absent, :name=>"dummy", :error=>"ok"} + end + + it "should list package correctly" do + @provider.expects(:pkg).with(:list, "-H", "dummy").returns "dummy 1.0@1.0-1.0 installed ----" + @provider.query.should == {:name => "dummy", :version => "1.0@1.0-1.0", + :ensure => :present, :status => "installed", + :provider => :pkg, :error => "ok"} + end +end diff --git a/spec/unit/provider/package/pkg_spec_spec.rb b/spec/unit/provider/package/pkg_spec_spec.rb deleted file mode 100644 index d0d933d5e..000000000 --- a/spec/unit/provider/package/pkg_spec_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -provider = Puppet::Type.type(:package).provider(:pkg) - -describe provider do - before do - @resource = stub 'resource', :[] => "dummy" - @provider = provider.new(@resource) - - @fakeresult = "install ok installed dummy 1.0\n" - end - - def self.it_should_respond_to(*actions) - actions.each do |action| - it "should respond to :#{action}" do - @provider.should respond_to(action) - end - end - end - - it_should_respond_to :install, :uninstall, :update, :query, :latest - - it "should be versionable" do - provider.should_not be_versionable - end - - it "should use :install to update" do - @provider.expects(:install) - @provider.update - end - - it "should parse a line correctly" do - result = provider.parse_line("dummy 1.0@1.0-1.0 installed ----") - result.should == {:name => "dummy", :version => "1.0@1.0-1.0", - :ensure => :present, :status => "installed", - :provider => :pkg, :error => "ok"} - end - - it "should fail to parse an incorrect line" do - result = provider.parse_line("foo") - result.should be_nil - end - - it "should fail to list a missing package" do - @provider.expects(:pkg).with(:list, "-H", "dummy").returns "1" - @provider.query.should == {:status=>"missing", :ensure=>:absent, - :name=>"dummy", :error=>"ok"} - end - - it "should fail to list a package when it can't parse the output line" do - @provider.expects(:pkg).with(:list, "-H", "dummy").returns "failed" - @provider.query.should == {:status=>"missing", :ensure=>:absent, :name=>"dummy", :error=>"ok"} - end - - it "should list package correctly" do - @provider.expects(:pkg).with(:list, "-H", "dummy").returns "dummy 1.0@1.0-1.0 installed ----" - @provider.query.should == {:name => "dummy", :version => "1.0@1.0-1.0", - :ensure => :present, :status => "installed", - :provider => :pkg, :error => "ok"} - end -end diff --git a/spec/unit/provider/package/pkgdmg_spec.rb b/spec/unit/provider/package/pkgdmg_spec.rb new file mode 100755 index 000000000..3f007e01b --- /dev/null +++ b/spec/unit/provider/package/pkgdmg_spec.rb @@ -0,0 +1,84 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider = Puppet::Type.type(:package).provider(:pkgdmg) + +describe provider do + before do + @resource = stub 'resource', :[] => "dummypkgdmg" + @provider = provider.new(@resource) + + @fakemountpoint = "/tmp/dmg.foo" + @fakepkgfile = "/tmp/test.pkg" + @fakehdiutilinfo = {"system-entities" => [{"mount-point" => @fakemountpoint}] } + @fakehdiutilplist = Plist::Emit.dump(@fakehdiutilinfo) + + @hdiutilmountargs = ["mount", "-plist", "-nobrowse", "-readonly", + "-noidme", "-mountrandom", "/tmp"] + end + + it "should not be versionable" do + provider.versionable?.should be_false + end + + it "should not be uninstallable" do + provider.uninstallable?.should be_false + end + + describe "when installing it should fail when" do + it "no source is specified" do + @resource.stubs(:[]).with(:source).returns nil + lambda { @provider.install }.should raise_error(Puppet::Error) + end + + it "no name is specified" do + @resource.stubs(:[]).with(:name).returns nil + lambda { @provider.install }.should raise_error(Puppet::Error) + end + + it "the source does not end in .dmg or .pkg" do + @resource.stubs(:[]).with(:source).returns "notendingindotdmgorpkg" + lambda { @provider.install }.should raise_error(Puppet::Error) + end + + it "a disk image with no system entities is mounted" do + @provider.stubs(:[]).with(:hdiutil).returns "" + lambda { @provider.install }.should raise_error(Puppet::Error) + end + end + + # These tests shouldn't be this messy. The pkgdmg provider needs work... + describe "when installing a pkgdmg" do + before do + fh = mock 'filehandle' + fh.stubs(:path).yields "/tmp/foo" + @resource.stubs(:[]).with(:source).returns "foo.dmg" + File.stubs(:open).yields fh + end + + it "should call hdiutil to mount and eject the disk image" do + Dir.stubs(:entries).returns [] + @provider.class.expects(:hdiutil).with("eject", @fakemountpoint).returns 0 + @provider.class.expects(:hdiutil).with("mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", nil).returns @fakehdiutilplist + @provider.install + end + + it "should call installpkg if a pkg/mpkg is found on the dmg" do + Dir.stubs(:entries).returns ["foo.pkg"] + @provider.class.stubs(:hdiutil).returns @fakehdiutilplist + @provider.class.expects(:installpkg).with("#{@fakemountpoint}/foo.pkg", @resource[:name], "foo.dmg").returns "" + @provider.install + end + end + + describe "when installing flat pkg file" do + it "should call installpkg if a flat pkg file is found instead of a .dmg image" do + @resource.stubs(:[]).with(:source).returns "/tmp/test.pkg" + @resource.stubs(:[]).with(:name).returns "testpkg" + @provider.class.expects(:installpkgdmg).with("#{@fakepkgfile}", "testpkg").returns "" + @provider.install + end + end + +end diff --git a/spec/unit/provider/package/pkgdmg_spec_spec.rb b/spec/unit/provider/package/pkgdmg_spec_spec.rb deleted file mode 100755 index 3f007e01b..000000000 --- a/spec/unit/provider/package/pkgdmg_spec_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -provider = Puppet::Type.type(:package).provider(:pkgdmg) - -describe provider do - before do - @resource = stub 'resource', :[] => "dummypkgdmg" - @provider = provider.new(@resource) - - @fakemountpoint = "/tmp/dmg.foo" - @fakepkgfile = "/tmp/test.pkg" - @fakehdiutilinfo = {"system-entities" => [{"mount-point" => @fakemountpoint}] } - @fakehdiutilplist = Plist::Emit.dump(@fakehdiutilinfo) - - @hdiutilmountargs = ["mount", "-plist", "-nobrowse", "-readonly", - "-noidme", "-mountrandom", "/tmp"] - end - - it "should not be versionable" do - provider.versionable?.should be_false - end - - it "should not be uninstallable" do - provider.uninstallable?.should be_false - end - - describe "when installing it should fail when" do - it "no source is specified" do - @resource.stubs(:[]).with(:source).returns nil - lambda { @provider.install }.should raise_error(Puppet::Error) - end - - it "no name is specified" do - @resource.stubs(:[]).with(:name).returns nil - lambda { @provider.install }.should raise_error(Puppet::Error) - end - - it "the source does not end in .dmg or .pkg" do - @resource.stubs(:[]).with(:source).returns "notendingindotdmgorpkg" - lambda { @provider.install }.should raise_error(Puppet::Error) - end - - it "a disk image with no system entities is mounted" do - @provider.stubs(:[]).with(:hdiutil).returns "" - lambda { @provider.install }.should raise_error(Puppet::Error) - end - end - - # These tests shouldn't be this messy. The pkgdmg provider needs work... - describe "when installing a pkgdmg" do - before do - fh = mock 'filehandle' - fh.stubs(:path).yields "/tmp/foo" - @resource.stubs(:[]).with(:source).returns "foo.dmg" - File.stubs(:open).yields fh - end - - it "should call hdiutil to mount and eject the disk image" do - Dir.stubs(:entries).returns [] - @provider.class.expects(:hdiutil).with("eject", @fakemountpoint).returns 0 - @provider.class.expects(:hdiutil).with("mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", nil).returns @fakehdiutilplist - @provider.install - end - - it "should call installpkg if a pkg/mpkg is found on the dmg" do - Dir.stubs(:entries).returns ["foo.pkg"] - @provider.class.stubs(:hdiutil).returns @fakehdiutilplist - @provider.class.expects(:installpkg).with("#{@fakemountpoint}/foo.pkg", @resource[:name], "foo.dmg").returns "" - @provider.install - end - end - - describe "when installing flat pkg file" do - it "should call installpkg if a flat pkg file is found instead of a .dmg image" do - @resource.stubs(:[]).with(:source).returns "/tmp/test.pkg" - @resource.stubs(:[]).with(:name).returns "testpkg" - @provider.class.expects(:installpkgdmg).with("#{@fakepkgfile}", "testpkg").returns "" - @provider.install - end - end - -end diff --git a/spec/unit/provider/package/zypper_spec.rb b/spec/unit/provider/package/zypper_spec.rb new file mode 100644 index 000000000..be45a4cc7 --- /dev/null +++ b/spec/unit/provider/package/zypper_spec.rb @@ -0,0 +1,81 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:package).provider(:zypper) + +describe provider_class do + before(:each) do + # Create a mock resource + @resource = stub 'resource' + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name and source + @resource.stubs(:[]).with(:name).returns "mypackage" + @resource.stubs(:[]).with(:ensure).returns :installed + @resource.stubs(:command).with(:zypper).returns "/usr/bin/zypper" + + @provider = provider_class.new(@resource) + end + + it "should have an install method" do + @provider = provider_class.new + @provider.should respond_to(:install) + end + + it "should have a latest method" do + @provider = provider_class.new + @provider.should respond_to(:uninstall) + end + + it "should have an update method" do + @provider = provider_class.new + @provider.should respond_to(:update) + end + + it "should have a latest method" do + @provider = provider_class.new + @provider.should respond_to(:latest) + end + + describe "when installing" do + it "should use a command-line with versioned package'" do + @resource.stubs(:should).with(:ensure).returns "1.2.3-4.5.6" + @provider.expects(:zypper).with('--quiet', :install, '-l', '-y', 'mypackage-1.2.3-4.5.6') + @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" + @provider.install + end + + it "should use a command-line without versioned package" do + @resource.stubs(:should).with(:ensure).returns :latest + @provider.expects(:zypper).with('--quiet', :install, '-l', '-y', 'mypackage') + @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" + @provider.install + end + end + + describe "when updating" do + it "should call install method of instance" do + @provider.expects(:install) + @provider.update + end + end + + describe "when getting latest version" do + it "should return a version string" do + + fake_data = "Loading repository data... +Reading installed packages... +S | Repository | Name | Version | Arch +--+----------------+-----------------------+-----------------+------- +v | SLES11-Updates | cups | 1.1.1 | x86_64 +v | SLES11-Updates | mypackage | 1.3.9h-8.20.1 | x86_64" + + @provider.expects(:zypper).with("list-updates").returns fake_data + @provider.latest.should == "1.3.9h-8.20.1" + end + end + + end diff --git a/spec/unit/provider/package/zypper_spec_spec.rb b/spec/unit/provider/package/zypper_spec_spec.rb deleted file mode 100644 index be45a4cc7..000000000 --- a/spec/unit/provider/package/zypper_spec_spec.rb +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:package).provider(:zypper) - -describe provider_class do - before(:each) do - # Create a mock resource - @resource = stub 'resource' - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name and source - @resource.stubs(:[]).with(:name).returns "mypackage" - @resource.stubs(:[]).with(:ensure).returns :installed - @resource.stubs(:command).with(:zypper).returns "/usr/bin/zypper" - - @provider = provider_class.new(@resource) - end - - it "should have an install method" do - @provider = provider_class.new - @provider.should respond_to(:install) - end - - it "should have a latest method" do - @provider = provider_class.new - @provider.should respond_to(:uninstall) - end - - it "should have an update method" do - @provider = provider_class.new - @provider.should respond_to(:update) - end - - it "should have a latest method" do - @provider = provider_class.new - @provider.should respond_to(:latest) - end - - describe "when installing" do - it "should use a command-line with versioned package'" do - @resource.stubs(:should).with(:ensure).returns "1.2.3-4.5.6" - @provider.expects(:zypper).with('--quiet', :install, '-l', '-y', 'mypackage-1.2.3-4.5.6') - @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" - @provider.install - end - - it "should use a command-line without versioned package" do - @resource.stubs(:should).with(:ensure).returns :latest - @provider.expects(:zypper).with('--quiet', :install, '-l', '-y', 'mypackage') - @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" - @provider.install - end - end - - describe "when updating" do - it "should call install method of instance" do - @provider.expects(:install) - @provider.update - end - end - - describe "when getting latest version" do - it "should return a version string" do - - fake_data = "Loading repository data... -Reading installed packages... -S | Repository | Name | Version | Arch ---+----------------+-----------------------+-----------------+------- -v | SLES11-Updates | cups | 1.1.1 | x86_64 -v | SLES11-Updates | mypackage | 1.3.9h-8.20.1 | x86_64" - - @provider.expects(:zypper).with("list-updates").returns fake_data - @provider.latest.should == "1.3.9h-8.20.1" - end - end - - end diff --git a/spec/unit/provider/parsedfile_spec.rb b/spec/unit/provider/parsedfile_spec.rb new file mode 100755 index 000000000..f20b6b235 --- /dev/null +++ b/spec/unit/provider/parsedfile_spec.rb @@ -0,0 +1,95 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/parsedfile' + +# Most of the tests for this are still in test/ral/provider/parsedfile.rb. +describe Puppet::Provider::ParsedFile do + before do + @class = Class.new(Puppet::Provider::ParsedFile) + end + + describe "when looking up records loaded from disk" do + it "should return nil if no records have been loaded" do + @class.record?("foo").should be_nil + end + end + + describe "when generating a list of instances" do + it "should return an instance for each record parsed from all of the registered targets" do + @class.expects(:targets).returns %w{/one /two} + @class.stubs(:skip_record?).returns false + one = [:uno1, :uno2] + two = [:dos1, :dos2] + @class.expects(:prefetch_target).with("/one").returns one + @class.expects(:prefetch_target).with("/two").returns two + + results = [] + (one + two).each do |inst| + results << inst.to_s + "_instance" + @class.expects(:new).with(inst).returns(results[-1]) + end + + @class.instances.should == results + end + + it "should skip specified records" do + @class.expects(:targets).returns %w{/one} + @class.expects(:skip_record?).with(:uno).returns false + @class.expects(:skip_record?).with(:dos).returns true + one = [:uno, :dos] + @class.expects(:prefetch_target).returns one + + @class.expects(:new).with(:uno).returns "eh" + @class.expects(:new).with(:dos).never + + @class.instances + end + end + + describe "when flushing a file's records to disk" do + before do + # This way we start with some @records, like we would in real life. + @class.stubs(:retrieve).returns [] + @class.default_target = "/foo/bar" + @class.initvars + @class.prefetch + + @filetype = Puppet::Util::FileType.filetype(:flat).new("/my/file") + Puppet::Util::FileType.filetype(:flat).stubs(:new).with("/my/file").returns @filetype + + @filetype.stubs(:write) + end + + it "should back up the file being written if the filetype can be backed up" do + @filetype.expects(:backup) + + @class.flush_target("/my/file") + end + + it "should not try to back up the file if the filetype cannot be backed up" do + @filetype = Puppet::Util::FileType.filetype(:ram).new("/my/file") + Puppet::Util::FileType.filetype(:flat).expects(:new).returns @filetype + + @filetype.stubs(:write) + + @class.flush_target("/my/file") + end + + it "should not back up the file more than once between calls to 'prefetch'" do + @filetype.expects(:backup).once + + @class.flush_target("/my/file") + @class.flush_target("/my/file") + end + + it "should back the file up again once the file has been reread" do + @filetype.expects(:backup).times(2) + + @class.flush_target("/my/file") + @class.prefetch + @class.flush_target("/my/file") + end + end +end diff --git a/spec/unit/provider/parsedfile_spec_spec.rb b/spec/unit/provider/parsedfile_spec_spec.rb deleted file mode 100755 index f20b6b235..000000000 --- a/spec/unit/provider/parsedfile_spec_spec.rb +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/provider/parsedfile' - -# Most of the tests for this are still in test/ral/provider/parsedfile.rb. -describe Puppet::Provider::ParsedFile do - before do - @class = Class.new(Puppet::Provider::ParsedFile) - end - - describe "when looking up records loaded from disk" do - it "should return nil if no records have been loaded" do - @class.record?("foo").should be_nil - end - end - - describe "when generating a list of instances" do - it "should return an instance for each record parsed from all of the registered targets" do - @class.expects(:targets).returns %w{/one /two} - @class.stubs(:skip_record?).returns false - one = [:uno1, :uno2] - two = [:dos1, :dos2] - @class.expects(:prefetch_target).with("/one").returns one - @class.expects(:prefetch_target).with("/two").returns two - - results = [] - (one + two).each do |inst| - results << inst.to_s + "_instance" - @class.expects(:new).with(inst).returns(results[-1]) - end - - @class.instances.should == results - end - - it "should skip specified records" do - @class.expects(:targets).returns %w{/one} - @class.expects(:skip_record?).with(:uno).returns false - @class.expects(:skip_record?).with(:dos).returns true - one = [:uno, :dos] - @class.expects(:prefetch_target).returns one - - @class.expects(:new).with(:uno).returns "eh" - @class.expects(:new).with(:dos).never - - @class.instances - end - end - - describe "when flushing a file's records to disk" do - before do - # This way we start with some @records, like we would in real life. - @class.stubs(:retrieve).returns [] - @class.default_target = "/foo/bar" - @class.initvars - @class.prefetch - - @filetype = Puppet::Util::FileType.filetype(:flat).new("/my/file") - Puppet::Util::FileType.filetype(:flat).stubs(:new).with("/my/file").returns @filetype - - @filetype.stubs(:write) - end - - it "should back up the file being written if the filetype can be backed up" do - @filetype.expects(:backup) - - @class.flush_target("/my/file") - end - - it "should not try to back up the file if the filetype cannot be backed up" do - @filetype = Puppet::Util::FileType.filetype(:ram).new("/my/file") - Puppet::Util::FileType.filetype(:flat).expects(:new).returns @filetype - - @filetype.stubs(:write) - - @class.flush_target("/my/file") - end - - it "should not back up the file more than once between calls to 'prefetch'" do - @filetype.expects(:backup).once - - @class.flush_target("/my/file") - @class.flush_target("/my/file") - end - - it "should back the file up again once the file has been reread" do - @filetype.expects(:backup).times(2) - - @class.flush_target("/my/file") - @class.prefetch - @class.flush_target("/my/file") - end - end -end diff --git a/spec/unit/provider/selboolean_spec.rb b/spec/unit/provider/selboolean_spec.rb new file mode 100755 index 000000000..2004485ca --- /dev/null +++ b/spec/unit/provider/selboolean_spec.rb @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider_class = Puppet::Type.type(:selboolean).provider(:getsetsebool) + +describe provider_class do + before :each do + @resource = stub("resource", :name => "foo") + @resource.stubs(:[]).returns "foo" + @provider = provider_class.new(@resource) + end + + it "should return :on when getsebool returns on" do + @provider.expects(:getsebool).with("foo").returns "foo --> on\n" + @provider.value.should == :on + end + + it "should return :off when getsebool returns on" do + @provider.expects(:getsebool).with("foo").returns "foo --> off\n" + @provider.value.should == :off + end + + it "should call execpipe when updating boolean setting" do + @provider.expects(:command).with(:setsebool).returns "/usr/sbin/setsebool" + @provider.expects(:execpipe).with("/usr/sbin/setsebool foo off") + @provider.value = :off + end + + it "should call execpipe with -P when updating persistent boolean setting" do + @resource.stubs(:[]).with(:persistent).returns :true + @provider.expects(:command).with(:setsebool).returns "/usr/sbin/setsebool" + @provider.expects(:execpipe).with("/usr/sbin/setsebool -P foo off") + @provider.value = :off + end + +end diff --git a/spec/unit/provider/selboolean_spec_spec.rb b/spec/unit/provider/selboolean_spec_spec.rb deleted file mode 100755 index 2004485ca..000000000 --- a/spec/unit/provider/selboolean_spec_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -provider_class = Puppet::Type.type(:selboolean).provider(:getsetsebool) - -describe provider_class do - before :each do - @resource = stub("resource", :name => "foo") - @resource.stubs(:[]).returns "foo" - @provider = provider_class.new(@resource) - end - - it "should return :on when getsebool returns on" do - @provider.expects(:getsebool).with("foo").returns "foo --> on\n" - @provider.value.should == :on - end - - it "should return :off when getsebool returns on" do - @provider.expects(:getsebool).with("foo").returns "foo --> off\n" - @provider.value.should == :off - end - - it "should call execpipe when updating boolean setting" do - @provider.expects(:command).with(:setsebool).returns "/usr/sbin/setsebool" - @provider.expects(:execpipe).with("/usr/sbin/setsebool foo off") - @provider.value = :off - end - - it "should call execpipe with -P when updating persistent boolean setting" do - @resource.stubs(:[]).with(:persistent).returns :true - @provider.expects(:command).with(:setsebool).returns "/usr/sbin/setsebool" - @provider.expects(:execpipe).with("/usr/sbin/setsebool -P foo off") - @provider.value = :off - end - -end diff --git a/spec/unit/provider/selmodule_spec.rb b/spec/unit/provider/selmodule_spec.rb new file mode 100755 index 000000000..5f60322d8 --- /dev/null +++ b/spec/unit/provider/selmodule_spec.rb @@ -0,0 +1,66 @@ +#!/usr/bin/env ruby + +# Note: This unit test depends on having a sample SELinux policy file +# in the same directory as this test called selmodule-example.pp +# with version 1.5.0. The provided selmodule-example.pp is the first +# 256 bytes taken from /usr/share/selinux/targeted/nagios.pp on Fedora 9 + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider_class = Puppet::Type.type(:selmodule).provider(:semodule) + +describe provider_class do + before :each do + @resource = stub("resource", :name => "foo") + @resource.stubs(:[]).returns "foo" + @provider = provider_class.new(@resource) + end + + describe "exists? method" do + it "should find a module if it is already loaded" do + @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" + @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields ["bar\t1.2.3\n", "foo\t4.4.4\n", "bang\t1.0.0\n"] + @provider.exists?.should == :true + end + + it "should return nil if not loaded" do + @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" + @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields ["bar\t1.2.3\n", "bang\t1.0.0\n"] + @provider.exists?.should be_nil + end + + it "should return nil if no modules are loaded" do + @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" + @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields [] + @provider.exists?.should be_nil + end + end + + describe "selmodversion_file" do + it "should return 1.5.0 for the example policy file" do + @provider.expects(:selmod_name_to_filename).returns "#{File.dirname(__FILE__)}/selmodule-example.pp" + @provider.selmodversion_file.should == "1.5.0" + end + end + + describe "syncversion" do + it "should return :true if loaded and file modules are in sync" do + @provider.expects(:selmodversion_loaded).returns "1.5.0" + @provider.expects(:selmodversion_file).returns "1.5.0" + @provider.syncversion.should == :true + end + + it "should return :false if loaded and file modules are not in sync" do + @provider.expects(:selmodversion_loaded).returns "1.4.0" + @provider.expects(:selmodversion_file).returns "1.5.0" + @provider.syncversion.should == :false + end + + it "should return before checking file version if no loaded policy" do + @provider.expects(:selmodversion_loaded).returns nil + @provider.syncversion.should == :false + end + + end + +end diff --git a/spec/unit/provider/selmodule_spec_spec.rb b/spec/unit/provider/selmodule_spec_spec.rb deleted file mode 100755 index 5f60322d8..000000000 --- a/spec/unit/provider/selmodule_spec_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env ruby - -# Note: This unit test depends on having a sample SELinux policy file -# in the same directory as this test called selmodule-example.pp -# with version 1.5.0. The provided selmodule-example.pp is the first -# 256 bytes taken from /usr/share/selinux/targeted/nagios.pp on Fedora 9 - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -provider_class = Puppet::Type.type(:selmodule).provider(:semodule) - -describe provider_class do - before :each do - @resource = stub("resource", :name => "foo") - @resource.stubs(:[]).returns "foo" - @provider = provider_class.new(@resource) - end - - describe "exists? method" do - it "should find a module if it is already loaded" do - @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" - @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields ["bar\t1.2.3\n", "foo\t4.4.4\n", "bang\t1.0.0\n"] - @provider.exists?.should == :true - end - - it "should return nil if not loaded" do - @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" - @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields ["bar\t1.2.3\n", "bang\t1.0.0\n"] - @provider.exists?.should be_nil - end - - it "should return nil if no modules are loaded" do - @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" - @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields [] - @provider.exists?.should be_nil - end - end - - describe "selmodversion_file" do - it "should return 1.5.0 for the example policy file" do - @provider.expects(:selmod_name_to_filename).returns "#{File.dirname(__FILE__)}/selmodule-example.pp" - @provider.selmodversion_file.should == "1.5.0" - end - end - - describe "syncversion" do - it "should return :true if loaded and file modules are in sync" do - @provider.expects(:selmodversion_loaded).returns "1.5.0" - @provider.expects(:selmodversion_file).returns "1.5.0" - @provider.syncversion.should == :true - end - - it "should return :false if loaded and file modules are not in sync" do - @provider.expects(:selmodversion_loaded).returns "1.4.0" - @provider.expects(:selmodversion_file).returns "1.5.0" - @provider.syncversion.should == :false - end - - it "should return before checking file version if no loaded policy" do - @provider.expects(:selmodversion_loaded).returns nil - @provider.syncversion.should == :false - end - - end - -end diff --git a/spec/unit/provider/service/daemontools_spec.rb b/spec/unit/provider/service/daemontools_spec.rb new file mode 100755 index 000000000..a2198bb9d --- /dev/null +++ b/spec/unit/provider/service/daemontools_spec.rb @@ -0,0 +1,166 @@ +#!/usr/bin/env ruby +# +# Unit testing for the Daemontools service Provider +# +# author Brice Figureau +# +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:service).provider(:daemontools) + +describe provider_class do + + before(:each) do + # Create a mock resource + @resource = stub 'resource' + + @provider = provider_class.new + @servicedir = "/etc/service" + @provider.servicedir=@servicedir + @daemondir = "/var/lib/service" + @provider.class.defpath=@daemondir + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name, source and path (because we won't run + # the thing that will fetch the resource path from the provider) + @resource.stubs(:[]).with(:name).returns "myservice" + @resource.stubs(:[]).with(:ensure).returns :enabled + @resource.stubs(:[]).with(:path).returns @daemondir + @resource.stubs(:ref).returns "Service[myservice]" + + @provider.resource = @resource + + @provider.stubs(:command).with(:svc).returns "svc" + @provider.stubs(:command).with(:svstat).returns "svstat" + + @provider.stubs(:svc) + @provider.stubs(:svstat) + end + + it "should have a restart method" do + @provider.should respond_to(:restart) + end + + it "should have a start method" do + @provider.should respond_to(:start) + end + + it "should have a stop method" do + @provider.should respond_to(:stop) + end + + it "should have an enabled? method" do + @provider.should respond_to(:enabled?) + end + + it "should have an enable method" do + @provider.should respond_to(:enable) + end + + it "should have a disable method" do + @provider.should respond_to(:disable) + end + + describe "when starting" do + it "should use 'svc' to start the service" do + @provider.stubs(:enabled?).returns :true + @provider.expects(:svc).with("-u", "/etc/service/myservice") + + @provider.start + end + + it "should enable the service if it is not enabled" do + @provider.stubs(:svc) + + @provider.expects(:enabled?).returns :false + @provider.expects(:enable) + + @provider.start + end + end + + describe "when stopping" do + it "should use 'svc' to stop the service" do + @provider.stubs(:disable) + @provider.expects(:svc).with("-d", "/etc/service/myservice") + + @provider.stop + end + end + + describe "when restarting" do + it "should use 'svc' to restart the service" do + @provider.expects(:svc).with("-t", "/etc/service/myservice") + + @provider.restart + end + end + + describe "when enabling" do + it "should create a symlink between daemon dir and service dir" do + FileTest.stubs(:symlink?).returns(false) + File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0) + @provider.enable + end + end + + describe "when disabling" do + it "should remove the symlink between daemon dir and service dir" do + FileTest.stubs(:directory?).returns(false) + FileTest.stubs(:symlink?).returns(true) + File.expects(:unlink).with(File.join(@servicedir,"myservice")) + @provider.stubs(:texecute).returns("") + @provider.disable + end + + it "should stop the service" do + FileTest.stubs(:directory?).returns(false) + FileTest.stubs(:symlink?).returns(true) + File.stubs(:unlink) + @provider.expects(:stop) + @provider.disable + end + end + + describe "when checking if the service is enabled?" do + it "should return true if it is running" do + @provider.stubs(:status).returns(:running) + + @provider.enabled?.should == :true + end + + [true, false].each do |t| + it "should return #{t} if the symlink exists" do + @provider.stubs(:status).returns(:stopped) + FileTest.stubs(:symlink?).returns(t) + + @provider.enabled?.should == "#{t}".to_sym + end + end + end + + describe "when checking status" do + it "should call the external command 'svstat /etc/service/myservice'" do + @provider.expects(:svstat).with(File.join(@servicedir,"myservice")) + @provider.status + end + end + + describe "when checking status" do + it "and svstat fails, properly raise a Puppet::Error" do + @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).raises(Puppet::ExecutionFailure, "failure") + lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: failure') + end + it "and svstat returns up, then return :running" do + @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice: up (pid 454) 954326 seconds") + @provider.status.should == :running + end + it "and svstat returns not running, then return :stopped" do + @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice: supervise not running") + @provider.status.should == :stopped + end + end + + end diff --git a/spec/unit/provider/service/daemontools_spec_spec.rb b/spec/unit/provider/service/daemontools_spec_spec.rb deleted file mode 100755 index a2198bb9d..000000000 --- a/spec/unit/provider/service/daemontools_spec_spec.rb +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env ruby -# -# Unit testing for the Daemontools service Provider -# -# author Brice Figureau -# -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:service).provider(:daemontools) - -describe provider_class do - - before(:each) do - # Create a mock resource - @resource = stub 'resource' - - @provider = provider_class.new - @servicedir = "/etc/service" - @provider.servicedir=@servicedir - @daemondir = "/var/lib/service" - @provider.class.defpath=@daemondir - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name, source and path (because we won't run - # the thing that will fetch the resource path from the provider) - @resource.stubs(:[]).with(:name).returns "myservice" - @resource.stubs(:[]).with(:ensure).returns :enabled - @resource.stubs(:[]).with(:path).returns @daemondir - @resource.stubs(:ref).returns "Service[myservice]" - - @provider.resource = @resource - - @provider.stubs(:command).with(:svc).returns "svc" - @provider.stubs(:command).with(:svstat).returns "svstat" - - @provider.stubs(:svc) - @provider.stubs(:svstat) - end - - it "should have a restart method" do - @provider.should respond_to(:restart) - end - - it "should have a start method" do - @provider.should respond_to(:start) - end - - it "should have a stop method" do - @provider.should respond_to(:stop) - end - - it "should have an enabled? method" do - @provider.should respond_to(:enabled?) - end - - it "should have an enable method" do - @provider.should respond_to(:enable) - end - - it "should have a disable method" do - @provider.should respond_to(:disable) - end - - describe "when starting" do - it "should use 'svc' to start the service" do - @provider.stubs(:enabled?).returns :true - @provider.expects(:svc).with("-u", "/etc/service/myservice") - - @provider.start - end - - it "should enable the service if it is not enabled" do - @provider.stubs(:svc) - - @provider.expects(:enabled?).returns :false - @provider.expects(:enable) - - @provider.start - end - end - - describe "when stopping" do - it "should use 'svc' to stop the service" do - @provider.stubs(:disable) - @provider.expects(:svc).with("-d", "/etc/service/myservice") - - @provider.stop - end - end - - describe "when restarting" do - it "should use 'svc' to restart the service" do - @provider.expects(:svc).with("-t", "/etc/service/myservice") - - @provider.restart - end - end - - describe "when enabling" do - it "should create a symlink between daemon dir and service dir" do - FileTest.stubs(:symlink?).returns(false) - File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0) - @provider.enable - end - end - - describe "when disabling" do - it "should remove the symlink between daemon dir and service dir" do - FileTest.stubs(:directory?).returns(false) - FileTest.stubs(:symlink?).returns(true) - File.expects(:unlink).with(File.join(@servicedir,"myservice")) - @provider.stubs(:texecute).returns("") - @provider.disable - end - - it "should stop the service" do - FileTest.stubs(:directory?).returns(false) - FileTest.stubs(:symlink?).returns(true) - File.stubs(:unlink) - @provider.expects(:stop) - @provider.disable - end - end - - describe "when checking if the service is enabled?" do - it "should return true if it is running" do - @provider.stubs(:status).returns(:running) - - @provider.enabled?.should == :true - end - - [true, false].each do |t| - it "should return #{t} if the symlink exists" do - @provider.stubs(:status).returns(:stopped) - FileTest.stubs(:symlink?).returns(t) - - @provider.enabled?.should == "#{t}".to_sym - end - end - end - - describe "when checking status" do - it "should call the external command 'svstat /etc/service/myservice'" do - @provider.expects(:svstat).with(File.join(@servicedir,"myservice")) - @provider.status - end - end - - describe "when checking status" do - it "and svstat fails, properly raise a Puppet::Error" do - @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).raises(Puppet::ExecutionFailure, "failure") - lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: failure') - end - it "and svstat returns up, then return :running" do - @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice: up (pid 454) 954326 seconds") - @provider.status.should == :running - end - it "and svstat returns not running, then return :stopped" do - @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice: supervise not running") - @provider.status.should == :stopped - end - end - - end diff --git a/spec/unit/provider/service/debian_spec.rb b/spec/unit/provider/service/debian_spec.rb new file mode 100755 index 000000000..ea87c375d --- /dev/null +++ b/spec/unit/provider/service/debian_spec.rb @@ -0,0 +1,89 @@ +#!/usr/bin/env ruby +# +# Unit testing for the debian service provider +# + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:service).provider(:debian) + +describe provider_class do + + before(:each) do + # Create a mock resource + @resource = stub 'resource' + + @provider = provider_class.new + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name, source and path + @resource.stubs(:[]).with(:name).returns "myservice" + @resource.stubs(:[]).with(:ensure).returns :enabled + @resource.stubs(:ref).returns "Service[myservice]" + + @provider.resource = @resource + + @provider.stubs(:command).with(:update_rc).returns "update_rc" + @provider.stubs(:command).with(:invoke_rc).returns "invoke_rc" + + @provider.stubs(:update_rc) + @provider.stubs(:invoke_rc) + end + + it "should have an enabled? method" do + @provider.should respond_to(:enabled?) + end + + it "should have an enable method" do + @provider.should respond_to(:enable) + end + + it "should have a disable method" do + @provider.should respond_to(:disable) + end + + describe "when enabling" do + it "should call update-rc.d twice" do + @provider.expects(:update_rc).twice + @provider.enable + end + end + + describe "when disabling" do + it "should call update-rc.d twice" do + @provider.expects(:update_rc).twice + @provider.disable + end + end + + describe "when checking whether it is enabled" do + it "should call Kernel.system() with the appropriate parameters" do + @provider.expects(:system).with("/usr/sbin/invoke-rc.d", "--quiet", "--query", @resource[:name], "start").once + @provider.enabled? + end + + it "should return true when invoke-rc.d exits with 104 status" do + @provider.stubs(:system) + $?.stubs(:exitstatus).returns(104) + @provider.enabled?.should == :true + end + + it "should return true when invoke-rc.d exits with 106 status" do + @provider.stubs(:system) + $?.stubs(:exitstatus).returns(106) + @provider.enabled?.should == :true + end + + # pick a range of non-[104.106] numbers, strings and booleans to test with. + [-100, -1, 0, 1, 100, "foo", "", :true, :false].each do |exitstatus| + it "should return false when invoke-rc.d exits with #{exitstatus} status" do + @provider.stubs(:system) + $?.stubs(:exitstatus).returns(exitstatus) + @provider.enabled?.should == :false + end + end + end + + end diff --git a/spec/unit/provider/service/debian_spec_spec.rb b/spec/unit/provider/service/debian_spec_spec.rb deleted file mode 100755 index ea87c375d..000000000 --- a/spec/unit/provider/service/debian_spec_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env ruby -# -# Unit testing for the debian service provider -# - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:service).provider(:debian) - -describe provider_class do - - before(:each) do - # Create a mock resource - @resource = stub 'resource' - - @provider = provider_class.new - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name, source and path - @resource.stubs(:[]).with(:name).returns "myservice" - @resource.stubs(:[]).with(:ensure).returns :enabled - @resource.stubs(:ref).returns "Service[myservice]" - - @provider.resource = @resource - - @provider.stubs(:command).with(:update_rc).returns "update_rc" - @provider.stubs(:command).with(:invoke_rc).returns "invoke_rc" - - @provider.stubs(:update_rc) - @provider.stubs(:invoke_rc) - end - - it "should have an enabled? method" do - @provider.should respond_to(:enabled?) - end - - it "should have an enable method" do - @provider.should respond_to(:enable) - end - - it "should have a disable method" do - @provider.should respond_to(:disable) - end - - describe "when enabling" do - it "should call update-rc.d twice" do - @provider.expects(:update_rc).twice - @provider.enable - end - end - - describe "when disabling" do - it "should call update-rc.d twice" do - @provider.expects(:update_rc).twice - @provider.disable - end - end - - describe "when checking whether it is enabled" do - it "should call Kernel.system() with the appropriate parameters" do - @provider.expects(:system).with("/usr/sbin/invoke-rc.d", "--quiet", "--query", @resource[:name], "start").once - @provider.enabled? - end - - it "should return true when invoke-rc.d exits with 104 status" do - @provider.stubs(:system) - $?.stubs(:exitstatus).returns(104) - @provider.enabled?.should == :true - end - - it "should return true when invoke-rc.d exits with 106 status" do - @provider.stubs(:system) - $?.stubs(:exitstatus).returns(106) - @provider.enabled?.should == :true - end - - # pick a range of non-[104.106] numbers, strings and booleans to test with. - [-100, -1, 0, 1, 100, "foo", "", :true, :false].each do |exitstatus| - it "should return false when invoke-rc.d exits with #{exitstatus} status" do - @provider.stubs(:system) - $?.stubs(:exitstatus).returns(exitstatus) - @provider.enabled?.should == :false - end - end - end - - end diff --git a/spec/unit/provider/service/init_spec.rb b/spec/unit/provider/service/init_spec.rb new file mode 100755 index 000000000..32bfaa204 --- /dev/null +++ b/spec/unit/provider/service/init_spec.rb @@ -0,0 +1,121 @@ +#!/usr/bin/env ruby +# +# Unit testing for the Init service Provider +# + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:service).provider(:init) + +describe provider_class do + + before :each do + @resource = stub 'resource' + @resource.stubs(:[]).returns(nil) + @resource.stubs(:[]).with(:name).returns "myservice" +# @resource.stubs(:[]).with(:ensure).returns :enabled + @resource.stubs(:[]).with(:path).returns ["/service/path","/alt/service/path"] +# @resource.stubs(:ref).returns "Service[myservice]" + File.stubs(:directory?).returns(true) + + @provider = provider_class.new + @provider.resource = @resource + end + + + describe "when searching for the init script" do + it "should discard paths that do not exist" do + File.stubs(:exist?).returns(false) + File.stubs(:directory?).returns(false) + @provider.paths.should be_empty + end + + it "should discard paths that are not directories" do + File.stubs(:exist?).returns(true) + File.stubs(:directory?).returns(false) + @provider.paths.should be_empty + end + + it "should be able to find the init script in the service path" do + File.expects(:stat).with("/service/path/myservice").returns true + @provider.initscript.should == "/service/path/myservice" + end + it "should be able to find the init script in the service path" do + File.expects(:stat).with("/alt/service/path/myservice").returns true + @provider.initscript.should == "/alt/service/path/myservice" + end + it "should fail if the service isn't there" do + lambda { @provider.initscript }.should raise_error(Puppet::Error, "Could not find init script for 'myservice'") + end + end + + describe "if the init script is present" do + before :each do + File.stubs(:stat).with("/service/path/myservice").returns true + end + + [:start, :stop, :status, :restart].each do |method| + it "should have a #{method} method" do + @provider.should respond_to(method) + end + describe "when running #{method}" do + + it "should use any provided explicit command" do + @resource.stubs(:[]).with(method).returns "/user/specified/command" + @provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } + @provider.send(method) + end + + it "should pass #{method} to the init script when no explicit command is provided" do + @resource.stubs(:[]).with("has#{method}".intern).returns :true + @provider.expects(:execute).with { |command, *args| command == ["/service/path/myservice",method]} + @provider.send(method) + end + end + end + + describe "when checking status" do + describe "when hasstatus is :true" do + before :each do + @resource.stubs(:[]).with(:hasstatus).returns :true + end + it "should execute the command" do + @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") + @provider.status + end + it "should consider the process running if the command returns 0" do + @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") + $?.stubs(:exitstatus).returns(0) + @provider.status.should == :running + end + [-10,-1,1,10].each { |ec| + it "should consider the process stopped if the command returns something non-0" do + @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") + $?.stubs(:exitstatus).returns(ec) + @provider.status.should == :stopped + end + } + end + describe "when hasstatus is not :true" do + it "should consider the service :running if it has a pid" do + @provider.expects(:getpid).returns "1234" + @provider.status.should == :running + end + it "should consider the service :stopped if it doesn't have a pid" do + @provider.expects(:getpid).returns nil + @provider.status.should == :stopped + end + end + end + + describe "when restarting and hasrestart is not :true" do + it "should stop and restart the process" do + @provider.expects(:texecute).with(:stop, ['/service/path/myservice', :stop ], true).returns("") + @provider.expects(:texecute).with(:start,['/service/path/myservice', :start], true).returns("") + $?.stubs(:exitstatus).returns(0) + @provider.restart + end + end + + end +end diff --git a/spec/unit/provider/service/init_spec_spec.rb b/spec/unit/provider/service/init_spec_spec.rb deleted file mode 100755 index 32bfaa204..000000000 --- a/spec/unit/provider/service/init_spec_spec.rb +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env ruby -# -# Unit testing for the Init service Provider -# - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:service).provider(:init) - -describe provider_class do - - before :each do - @resource = stub 'resource' - @resource.stubs(:[]).returns(nil) - @resource.stubs(:[]).with(:name).returns "myservice" -# @resource.stubs(:[]).with(:ensure).returns :enabled - @resource.stubs(:[]).with(:path).returns ["/service/path","/alt/service/path"] -# @resource.stubs(:ref).returns "Service[myservice]" - File.stubs(:directory?).returns(true) - - @provider = provider_class.new - @provider.resource = @resource - end - - - describe "when searching for the init script" do - it "should discard paths that do not exist" do - File.stubs(:exist?).returns(false) - File.stubs(:directory?).returns(false) - @provider.paths.should be_empty - end - - it "should discard paths that are not directories" do - File.stubs(:exist?).returns(true) - File.stubs(:directory?).returns(false) - @provider.paths.should be_empty - end - - it "should be able to find the init script in the service path" do - File.expects(:stat).with("/service/path/myservice").returns true - @provider.initscript.should == "/service/path/myservice" - end - it "should be able to find the init script in the service path" do - File.expects(:stat).with("/alt/service/path/myservice").returns true - @provider.initscript.should == "/alt/service/path/myservice" - end - it "should fail if the service isn't there" do - lambda { @provider.initscript }.should raise_error(Puppet::Error, "Could not find init script for 'myservice'") - end - end - - describe "if the init script is present" do - before :each do - File.stubs(:stat).with("/service/path/myservice").returns true - end - - [:start, :stop, :status, :restart].each do |method| - it "should have a #{method} method" do - @provider.should respond_to(method) - end - describe "when running #{method}" do - - it "should use any provided explicit command" do - @resource.stubs(:[]).with(method).returns "/user/specified/command" - @provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } - @provider.send(method) - end - - it "should pass #{method} to the init script when no explicit command is provided" do - @resource.stubs(:[]).with("has#{method}".intern).returns :true - @provider.expects(:execute).with { |command, *args| command == ["/service/path/myservice",method]} - @provider.send(method) - end - end - end - - describe "when checking status" do - describe "when hasstatus is :true" do - before :each do - @resource.stubs(:[]).with(:hasstatus).returns :true - end - it "should execute the command" do - @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") - @provider.status - end - it "should consider the process running if the command returns 0" do - @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") - $?.stubs(:exitstatus).returns(0) - @provider.status.should == :running - end - [-10,-1,1,10].each { |ec| - it "should consider the process stopped if the command returns something non-0" do - @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") - $?.stubs(:exitstatus).returns(ec) - @provider.status.should == :stopped - end - } - end - describe "when hasstatus is not :true" do - it "should consider the service :running if it has a pid" do - @provider.expects(:getpid).returns "1234" - @provider.status.should == :running - end - it "should consider the service :stopped if it doesn't have a pid" do - @provider.expects(:getpid).returns nil - @provider.status.should == :stopped - end - end - end - - describe "when restarting and hasrestart is not :true" do - it "should stop and restart the process" do - @provider.expects(:texecute).with(:stop, ['/service/path/myservice', :stop ], true).returns("") - @provider.expects(:texecute).with(:start,['/service/path/myservice', :start], true).returns("") - $?.stubs(:exitstatus).returns(0) - @provider.restart - end - end - - end -end diff --git a/spec/unit/provider/service/launchd_spec.rb b/spec/unit/provider/service/launchd_spec.rb new file mode 100755 index 000000000..fa86a21a5 --- /dev/null +++ b/spec/unit/provider/service/launchd_spec.rb @@ -0,0 +1,200 @@ +#!/usr/bin/env ruby +# +# Unit testing for the launchd service provider +# + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet' + +provider_class = Puppet::Type.type(:service).provider(:launchd) + +describe provider_class do + + before :each do + # Create a mock resource + @resource = stub 'resource' + + @provider = provider_class.new + @joblabel = "com.foo.food" + @jobplist = {} + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name, ensure and enable + @resource.stubs(:[]).with(:name).returns @joblabel + @resource.stubs(:[]).with(:ensure).returns :enabled + @resource.stubs(:[]).with(:enable).returns :true + @resource.stubs(:ref).returns "Service[#{@joblabel}]" + + # stub out the provider methods that actually touch the filesystem + # or execute commands + @provider.stubs(:plist_from_label).returns([@joblabel, @jobplist]) + @provider.stubs(:execute).returns("") + @provider.stubs(:resource).returns @resource + + # We stub this out for the normal case as 10.6 is "special". + provider_class.stubs(:get_macosx_version_major).returns("10.5") + + end + + it "should have a start method for #{@provider.object_id}" do + @provider.should respond_to(:start) + end + + it "should have a stop method" do + @provider.should respond_to(:stop) + end + + it "should have an enabled? method" do + @provider.should respond_to(:enabled?) + end + + it "should have an enable method" do + @provider.should respond_to(:enable) + end + + it "should have a disable method" do + @provider.should respond_to(:disable) + end + + it "should have a status method" do + @provider.should respond_to(:status) + end + + + describe "when checking status" do + it "should call the external command 'launchctl list' once" do + @provider.expects(:launchctl).with(:list).returns("rotating-strawberry-madonnas") + @provider.status + end + it "should return stopped if not listed in launchctl list output" do + @provider.stubs(:launchctl).with(:list).returns("rotating-strawberry-madonnas") + @provider.status.should == :stopped + end + it "should return running if listed in launchctl list output" do + @provider.stubs(:launchctl).with(:list).returns(@joblabel) + @provider.status.should == :running + end + end + + describe "when checking whether the service is enabled" do + it "should return true if the job plist says disabled is false" do + @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => false}]) + @provider.enabled?.should == :true + end + it "should return true if the job plist has no disabled key" do + @provider.stubs(:plist_from_label).returns(["foo", {}]) + @provider.enabled?.should == :true + end + it "should return false if the job plist says disabled is true" do + @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}]) + @provider.enabled?.should == :false + end + end + + describe "when checking whether the service is enabled on OS X 10.6" do + it "should return true if the job plist says disabled is true and the global overrides says disabled is false" do + provider_class.stubs(:get_macosx_version_major).returns("10.6") + @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}]) + Plist.stubs(:parse_xml).returns({@resource[:name] => {"Disabled" => false}}) + @provider.enabled?.should == :true + end + it "should return false if the job plist says disabled is false and the global overrides says disabled is true" do + provider_class.stubs(:get_macosx_version_major).returns("10.6") + @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => false}]) + Plist.stubs(:parse_xml).returns({@resource[:name] => {"Disabled" => true}}) + @provider.enabled?.should == :false + end + it "should return true if the job plist and the global overrides have no disabled keys" do + provider_class.stubs(:get_macosx_version_major).returns("10.6") + @provider.stubs(:plist_from_label).returns(["foo", {}]) + Plist.stubs(:parse_xml).returns({}) + @provider.enabled?.should == :true + end + end + + describe "when starting the service" do + it "should look for the relevant plist once" do + @provider.expects(:plist_from_label).once + @provider.start + end + it "should execute 'launchctl load' once without writing to the plist if the job is enabled" do + @provider.stubs(:enabled?).returns :true + @provider.expects(:execute).with([:launchctl, :load, @resource[:name]]).once + @provider.start + end + it "should execute 'launchctl load' with writing to the plist once if the job is disabled" do + @provider.stubs(:enabled?).returns :false + @provider.expects(:execute).with([:launchctl, :load, "-w", @resource[:name]]).once + @provider.start + end + it "should disable the job once if the job is disabled and should be disabled at boot" do + @provider.stubs(:enabled?).returns :false + @resource.stubs(:[]).with(:enable).returns :false + @provider.expects(:disable).once + @provider.start + end + end + + describe "when stopping the service" do + it "should look for the relevant plist once" do + @provider.expects(:plist_from_label).once + @provider.stop + end + it "should execute 'launchctl unload' once without writing to the plist if the job is disabled" do + @provider.stubs(:enabled?).returns :false + @provider.expects(:execute).with([:launchctl, :unload, @resource[:name]]).once + @provider.stop + end + it "should execute 'launchctl unload' with writing to the plist once if the job is enabled" do + @provider.stubs(:enabled?).returns :true + @provider.expects(:execute).with([:launchctl, :unload, "-w", @resource[:name]]).once + @provider.stop + end + it "should enable the job once if the job is enabled and should be enabled at boot" do + @provider.stubs(:enabled?).returns :true + @resource.stubs(:[]).with(:enable).returns :true + @provider.expects(:enable).once + @provider.stop + end + end + + describe "when enabling the service" do + it "should look for the relevant plist once" do + @provider.expects(:plist_from_label).once + @provider.stop + end + it "should check if the job is enabled once" do + @provider.expects(:enabled?).once + @provider.stop + end + end + + describe "when disabling the service" do + it "should look for the relevant plist once" do + @provider.expects(:plist_from_label).once + @provider.stop + end + end + + describe "when enabling the service on OS X 10.6" do + it "should write to the global launchd overrides file once" do + provider_class.stubs(:get_macosx_version_major).returns("10.6") + Plist.stubs(:parse_xml).returns({}) + Plist::Emit.expects(:save_plist).once + @provider.enable + end + end + + describe "when disabling the service on OS X 10.6" do + it "should write to the global launchd overrides file once" do + provider_class.stubs(:get_macosx_version_major).returns("10.6") + Plist.stubs(:parse_xml).returns({}) + Plist::Emit.expects(:save_plist).once + @provider.enable + end + end + + end diff --git a/spec/unit/provider/service/launchd_spec_spec.rb b/spec/unit/provider/service/launchd_spec_spec.rb deleted file mode 100755 index fa86a21a5..000000000 --- a/spec/unit/provider/service/launchd_spec_spec.rb +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env ruby -# -# Unit testing for the launchd service provider -# - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet' - -provider_class = Puppet::Type.type(:service).provider(:launchd) - -describe provider_class do - - before :each do - # Create a mock resource - @resource = stub 'resource' - - @provider = provider_class.new - @joblabel = "com.foo.food" - @jobplist = {} - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name, ensure and enable - @resource.stubs(:[]).with(:name).returns @joblabel - @resource.stubs(:[]).with(:ensure).returns :enabled - @resource.stubs(:[]).with(:enable).returns :true - @resource.stubs(:ref).returns "Service[#{@joblabel}]" - - # stub out the provider methods that actually touch the filesystem - # or execute commands - @provider.stubs(:plist_from_label).returns([@joblabel, @jobplist]) - @provider.stubs(:execute).returns("") - @provider.stubs(:resource).returns @resource - - # We stub this out for the normal case as 10.6 is "special". - provider_class.stubs(:get_macosx_version_major).returns("10.5") - - end - - it "should have a start method for #{@provider.object_id}" do - @provider.should respond_to(:start) - end - - it "should have a stop method" do - @provider.should respond_to(:stop) - end - - it "should have an enabled? method" do - @provider.should respond_to(:enabled?) - end - - it "should have an enable method" do - @provider.should respond_to(:enable) - end - - it "should have a disable method" do - @provider.should respond_to(:disable) - end - - it "should have a status method" do - @provider.should respond_to(:status) - end - - - describe "when checking status" do - it "should call the external command 'launchctl list' once" do - @provider.expects(:launchctl).with(:list).returns("rotating-strawberry-madonnas") - @provider.status - end - it "should return stopped if not listed in launchctl list output" do - @provider.stubs(:launchctl).with(:list).returns("rotating-strawberry-madonnas") - @provider.status.should == :stopped - end - it "should return running if listed in launchctl list output" do - @provider.stubs(:launchctl).with(:list).returns(@joblabel) - @provider.status.should == :running - end - end - - describe "when checking whether the service is enabled" do - it "should return true if the job plist says disabled is false" do - @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => false}]) - @provider.enabled?.should == :true - end - it "should return true if the job plist has no disabled key" do - @provider.stubs(:plist_from_label).returns(["foo", {}]) - @provider.enabled?.should == :true - end - it "should return false if the job plist says disabled is true" do - @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}]) - @provider.enabled?.should == :false - end - end - - describe "when checking whether the service is enabled on OS X 10.6" do - it "should return true if the job plist says disabled is true and the global overrides says disabled is false" do - provider_class.stubs(:get_macosx_version_major).returns("10.6") - @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}]) - Plist.stubs(:parse_xml).returns({@resource[:name] => {"Disabled" => false}}) - @provider.enabled?.should == :true - end - it "should return false if the job plist says disabled is false and the global overrides says disabled is true" do - provider_class.stubs(:get_macosx_version_major).returns("10.6") - @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => false}]) - Plist.stubs(:parse_xml).returns({@resource[:name] => {"Disabled" => true}}) - @provider.enabled?.should == :false - end - it "should return true if the job plist and the global overrides have no disabled keys" do - provider_class.stubs(:get_macosx_version_major).returns("10.6") - @provider.stubs(:plist_from_label).returns(["foo", {}]) - Plist.stubs(:parse_xml).returns({}) - @provider.enabled?.should == :true - end - end - - describe "when starting the service" do - it "should look for the relevant plist once" do - @provider.expects(:plist_from_label).once - @provider.start - end - it "should execute 'launchctl load' once without writing to the plist if the job is enabled" do - @provider.stubs(:enabled?).returns :true - @provider.expects(:execute).with([:launchctl, :load, @resource[:name]]).once - @provider.start - end - it "should execute 'launchctl load' with writing to the plist once if the job is disabled" do - @provider.stubs(:enabled?).returns :false - @provider.expects(:execute).with([:launchctl, :load, "-w", @resource[:name]]).once - @provider.start - end - it "should disable the job once if the job is disabled and should be disabled at boot" do - @provider.stubs(:enabled?).returns :false - @resource.stubs(:[]).with(:enable).returns :false - @provider.expects(:disable).once - @provider.start - end - end - - describe "when stopping the service" do - it "should look for the relevant plist once" do - @provider.expects(:plist_from_label).once - @provider.stop - end - it "should execute 'launchctl unload' once without writing to the plist if the job is disabled" do - @provider.stubs(:enabled?).returns :false - @provider.expects(:execute).with([:launchctl, :unload, @resource[:name]]).once - @provider.stop - end - it "should execute 'launchctl unload' with writing to the plist once if the job is enabled" do - @provider.stubs(:enabled?).returns :true - @provider.expects(:execute).with([:launchctl, :unload, "-w", @resource[:name]]).once - @provider.stop - end - it "should enable the job once if the job is enabled and should be enabled at boot" do - @provider.stubs(:enabled?).returns :true - @resource.stubs(:[]).with(:enable).returns :true - @provider.expects(:enable).once - @provider.stop - end - end - - describe "when enabling the service" do - it "should look for the relevant plist once" do - @provider.expects(:plist_from_label).once - @provider.stop - end - it "should check if the job is enabled once" do - @provider.expects(:enabled?).once - @provider.stop - end - end - - describe "when disabling the service" do - it "should look for the relevant plist once" do - @provider.expects(:plist_from_label).once - @provider.stop - end - end - - describe "when enabling the service on OS X 10.6" do - it "should write to the global launchd overrides file once" do - provider_class.stubs(:get_macosx_version_major).returns("10.6") - Plist.stubs(:parse_xml).returns({}) - Plist::Emit.expects(:save_plist).once - @provider.enable - end - end - - describe "when disabling the service on OS X 10.6" do - it "should write to the global launchd overrides file once" do - provider_class.stubs(:get_macosx_version_major).returns("10.6") - Plist.stubs(:parse_xml).returns({}) - Plist::Emit.expects(:save_plist).once - @provider.enable - end - end - - end diff --git a/spec/unit/provider/service/redhat_spec.rb b/spec/unit/provider/service/redhat_spec.rb new file mode 100755 index 000000000..47997dca3 --- /dev/null +++ b/spec/unit/provider/service/redhat_spec.rb @@ -0,0 +1,96 @@ +#!/usr/bin/env ruby +# +# Unit testing for the RedHat service Provider +# + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:service).provider(:redhat) + +describe provider_class do + + before :each do + @resource = stub 'resource' + @resource.stubs(:[]).returns(nil) + @resource.stubs(:[]).with(:name).returns "myservice" + + @provider = provider_class.new + @provider.resource = @resource + FileTest.stubs(:file?).with('/sbin/service').returns true + FileTest.stubs(:executable?).with('/sbin/service').returns true + end + + it "should have an enabled? method" do + @provider.should respond_to(:enabled?) + end + + it "should have an enable method" do + @provider.should respond_to(:enable) + end + + it "should have a disable method" do + @provider.should respond_to(:disable) + end + + [:start, :stop, :status, :restart].each do |method| + it "should have a #{method} method" do + @provider.should respond_to(method) + end + describe "when running #{method}" do + + it "should use any provided explicit command" do + @resource.stubs(:[]).with(method).returns "/user/specified/command" + @provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } + @provider.send(method) + end + + it "should execute the service script with #{method} when no explicit command is provided" do + @resource.stubs(:[]).with("has#{method}".intern).returns :true + @provider.expects(:execute).with { |command, *args| command == ['/sbin/service', 'myservice', method.to_s]} + @provider.send(method) + end + end + end + + describe "when checking status" do + describe "when hasstatus is :true" do + before :each do + @resource.stubs(:[]).with(:hasstatus).returns :true + end + it "should execute the service script with fail_on_failure false" do + @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) + @provider.status + end + it "should consider the process running if the command returns 0" do + @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) + $?.stubs(:exitstatus).returns(0) + @provider.status.should == :running + end + [-10,-1,1,10].each { |ec| + it "should consider the process stopped if the command returns something non-0" do + @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) + $?.stubs(:exitstatus).returns(ec) + @provider.status.should == :stopped + end + } + end + describe "when hasstatus is not :true" do + it "should consider the service :running if it has a pid" do + @provider.expects(:getpid).returns "1234" + @provider.status.should == :running + end + it "should consider the service :stopped if it doesn't have a pid" do + @provider.expects(:getpid).returns nil + @provider.status.should == :stopped + end + end + end + + describe "when restarting and hasrestart is not :true" do + it "should stop and restart the process with the server script" do + @provider.expects(:texecute).with(:stop, ['/sbin/service', 'myservice', 'stop'], true) + @provider.expects(:texecute).with(:start, ['/sbin/service', 'myservice', 'start'], true) + @provider.restart + end + end +end diff --git a/spec/unit/provider/service/redhat_spec_spec.rb b/spec/unit/provider/service/redhat_spec_spec.rb deleted file mode 100755 index 47997dca3..000000000 --- a/spec/unit/provider/service/redhat_spec_spec.rb +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env ruby -# -# Unit testing for the RedHat service Provider -# - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:service).provider(:redhat) - -describe provider_class do - - before :each do - @resource = stub 'resource' - @resource.stubs(:[]).returns(nil) - @resource.stubs(:[]).with(:name).returns "myservice" - - @provider = provider_class.new - @provider.resource = @resource - FileTest.stubs(:file?).with('/sbin/service').returns true - FileTest.stubs(:executable?).with('/sbin/service').returns true - end - - it "should have an enabled? method" do - @provider.should respond_to(:enabled?) - end - - it "should have an enable method" do - @provider.should respond_to(:enable) - end - - it "should have a disable method" do - @provider.should respond_to(:disable) - end - - [:start, :stop, :status, :restart].each do |method| - it "should have a #{method} method" do - @provider.should respond_to(method) - end - describe "when running #{method}" do - - it "should use any provided explicit command" do - @resource.stubs(:[]).with(method).returns "/user/specified/command" - @provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } - @provider.send(method) - end - - it "should execute the service script with #{method} when no explicit command is provided" do - @resource.stubs(:[]).with("has#{method}".intern).returns :true - @provider.expects(:execute).with { |command, *args| command == ['/sbin/service', 'myservice', method.to_s]} - @provider.send(method) - end - end - end - - describe "when checking status" do - describe "when hasstatus is :true" do - before :each do - @resource.stubs(:[]).with(:hasstatus).returns :true - end - it "should execute the service script with fail_on_failure false" do - @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) - @provider.status - end - it "should consider the process running if the command returns 0" do - @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) - $?.stubs(:exitstatus).returns(0) - @provider.status.should == :running - end - [-10,-1,1,10].each { |ec| - it "should consider the process stopped if the command returns something non-0" do - @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) - $?.stubs(:exitstatus).returns(ec) - @provider.status.should == :stopped - end - } - end - describe "when hasstatus is not :true" do - it "should consider the service :running if it has a pid" do - @provider.expects(:getpid).returns "1234" - @provider.status.should == :running - end - it "should consider the service :stopped if it doesn't have a pid" do - @provider.expects(:getpid).returns nil - @provider.status.should == :stopped - end - end - end - - describe "when restarting and hasrestart is not :true" do - it "should stop and restart the process with the server script" do - @provider.expects(:texecute).with(:stop, ['/sbin/service', 'myservice', 'stop'], true) - @provider.expects(:texecute).with(:start, ['/sbin/service', 'myservice', 'start'], true) - @provider.restart - end - end -end diff --git a/spec/unit/provider/service/runit_spec.rb b/spec/unit/provider/service/runit_spec.rb new file mode 100755 index 000000000..dd8d0dac1 --- /dev/null +++ b/spec/unit/provider/service/runit_spec.rb @@ -0,0 +1,140 @@ +#!/usr/bin/env ruby +# +# Unit testing for the Runit service Provider +# +# author Brice Figureau +# +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:service).provider(:runit) + +describe provider_class do + + before(:each) do + # Create a mock resource + @resource = stub 'resource' + + @provider = provider_class.new + @servicedir = "/etc/service" + @provider.servicedir=@servicedir + @daemondir = "/etc/sv" + @provider.class.defpath=@daemondir + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name, source and path (because we won't run + # the thing that will fetch the resource path from the provider) + @resource.stubs(:[]).with(:name).returns "myservice" + @resource.stubs(:[]).with(:ensure).returns :enabled + @resource.stubs(:[]).with(:path).returns @daemondir + @resource.stubs(:ref).returns "Service[myservice]" + + @provider.stubs(:sv) + + @provider.stubs(:resource).returns @resource + end + + it "should have a restart method" do + @provider.should respond_to(:restart) + end + + it "should have a restartcmd method" do + @provider.should respond_to(:restartcmd) + end + + it "should have a start method" do + @provider.should respond_to(:start) + end + + it "should have a stop method" do + @provider.should respond_to(:stop) + end + + it "should have an enabled? method" do + @provider.should respond_to(:enabled?) + end + + it "should have an enable method" do + @provider.should respond_to(:enable) + end + + it "should have a disable method" do + @provider.should respond_to(:disable) + end + + describe "when starting" do + it "should enable the service if it is not enabled" do + @provider.stubs(:sv) + + @provider.expects(:enabled?).returns :false + @provider.expects(:enable) + + @provider.start + end + + it "should execute external command 'sv start /etc/service/myservice'" do + @provider.stubs(:enabled?).returns :true + @provider.expects(:sv).with("start", "/etc/service/myservice") + @provider.start + end + end + + describe "when stopping" do + it "should execute external command 'sv stop /etc/service/myservice'" do + @provider.expects(:sv).with("stop", "/etc/service/myservice") + @provider.stop + end + end + + describe "when restarting" do + it "should call 'sv restart /etc/service/myservice'" do + @provider.expects(:sv).with("restart","/etc/service/myservice") + @provider.restart + end + end + + describe "when enabling" do + it "should create a symlink between daemon dir and service dir" do + FileTest.stubs(:symlink?).returns(false) + File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0) + @provider.enable + end + end + + describe "when disabling" do + it "should remove the '/etc/service/myservice' symlink" do + FileTest.stubs(:directory?).returns(false) + FileTest.stubs(:symlink?).returns(true) + File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0) + @provider.disable + end + end + + describe "when checking status" do + it "should call the external command 'sv status /etc/sv/myservice'" do + @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")) + @provider.status + end + end + + describe "when checking status" do + it "and sv status fails, properly raise a Puppet::Error" do + @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).raises(Puppet::ExecutionFailure, "fail: /etc/sv/myservice: file not found") + lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: fail: /etc/sv/myservice: file not found') + end + it "and sv status returns up, then return :running" do + @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("run: /etc/sv/myservice: (pid 9029) 6s") + @provider.status.should == :running + end + it "and sv status returns not running, then return :stopped" do + @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("fail: /etc/sv/myservice: runsv not running") + @provider.status.should == :stopped + end + it "and sv status returns a warning, then return :stopped" do + @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("warning: /etc/sv/myservice: unable to open supervise/ok: file does not exist") + @provider.status.should == :stopped + end + end + + end diff --git a/spec/unit/provider/service/runit_spec_spec.rb b/spec/unit/provider/service/runit_spec_spec.rb deleted file mode 100755 index dd8d0dac1..000000000 --- a/spec/unit/provider/service/runit_spec_spec.rb +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env ruby -# -# Unit testing for the Runit service Provider -# -# author Brice Figureau -# -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:service).provider(:runit) - -describe provider_class do - - before(:each) do - # Create a mock resource - @resource = stub 'resource' - - @provider = provider_class.new - @servicedir = "/etc/service" - @provider.servicedir=@servicedir - @daemondir = "/etc/sv" - @provider.class.defpath=@daemondir - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name, source and path (because we won't run - # the thing that will fetch the resource path from the provider) - @resource.stubs(:[]).with(:name).returns "myservice" - @resource.stubs(:[]).with(:ensure).returns :enabled - @resource.stubs(:[]).with(:path).returns @daemondir - @resource.stubs(:ref).returns "Service[myservice]" - - @provider.stubs(:sv) - - @provider.stubs(:resource).returns @resource - end - - it "should have a restart method" do - @provider.should respond_to(:restart) - end - - it "should have a restartcmd method" do - @provider.should respond_to(:restartcmd) - end - - it "should have a start method" do - @provider.should respond_to(:start) - end - - it "should have a stop method" do - @provider.should respond_to(:stop) - end - - it "should have an enabled? method" do - @provider.should respond_to(:enabled?) - end - - it "should have an enable method" do - @provider.should respond_to(:enable) - end - - it "should have a disable method" do - @provider.should respond_to(:disable) - end - - describe "when starting" do - it "should enable the service if it is not enabled" do - @provider.stubs(:sv) - - @provider.expects(:enabled?).returns :false - @provider.expects(:enable) - - @provider.start - end - - it "should execute external command 'sv start /etc/service/myservice'" do - @provider.stubs(:enabled?).returns :true - @provider.expects(:sv).with("start", "/etc/service/myservice") - @provider.start - end - end - - describe "when stopping" do - it "should execute external command 'sv stop /etc/service/myservice'" do - @provider.expects(:sv).with("stop", "/etc/service/myservice") - @provider.stop - end - end - - describe "when restarting" do - it "should call 'sv restart /etc/service/myservice'" do - @provider.expects(:sv).with("restart","/etc/service/myservice") - @provider.restart - end - end - - describe "when enabling" do - it "should create a symlink between daemon dir and service dir" do - FileTest.stubs(:symlink?).returns(false) - File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0) - @provider.enable - end - end - - describe "when disabling" do - it "should remove the '/etc/service/myservice' symlink" do - FileTest.stubs(:directory?).returns(false) - FileTest.stubs(:symlink?).returns(true) - File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0) - @provider.disable - end - end - - describe "when checking status" do - it "should call the external command 'sv status /etc/sv/myservice'" do - @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")) - @provider.status - end - end - - describe "when checking status" do - it "and sv status fails, properly raise a Puppet::Error" do - @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).raises(Puppet::ExecutionFailure, "fail: /etc/sv/myservice: file not found") - lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: fail: /etc/sv/myservice: file not found') - end - it "and sv status returns up, then return :running" do - @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("run: /etc/sv/myservice: (pid 9029) 6s") - @provider.status.should == :running - end - it "and sv status returns not running, then return :stopped" do - @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("fail: /etc/sv/myservice: runsv not running") - @provider.status.should == :stopped - end - it "and sv status returns a warning, then return :stopped" do - @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("warning: /etc/sv/myservice: unable to open supervise/ok: file does not exist") - @provider.status.should == :stopped - end - end - - end diff --git a/spec/unit/provider/service/src_spec.rb b/spec/unit/provider/service/src_spec.rb new file mode 100755 index 000000000..76a6cf850 --- /dev/null +++ b/spec/unit/provider/service/src_spec.rb @@ -0,0 +1,97 @@ +#!/usr/bin/env ruby +# +# Unit testing for the AIX System Resource Controller (src) provider +# + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:service).provider(:src) + +describe provider_class do + + before :each do + @resource = stub 'resource' + @resource.stubs(:[]).returns(nil) + @resource.stubs(:[]).with(:name).returns "myservice" + + @provider = provider_class.new + @provider.resource = @resource + + @provider.stubs(:command).with(:stopsrc).returns "/usr/bin/stopsrc" + @provider.stubs(:command).with(:startsrc).returns "/usr/bin/startsrc" + @provider.stubs(:command).with(:lssrc).returns "/usr/bin/lssrc" + @provider.stubs(:command).with(:refresh).returns "/usr/bin/refresh" + + @provider.stubs(:stopsrc) + @provider.stubs(:startsrc) + @provider.stubs(:lssrc) + @provider.stubs(:refresh) + end + + [:start, :stop, :status, :restart].each do |method| + it "should have a #{method} method" do + @provider.should respond_to(method) + end + end + + it "should execute the startsrc command" do + @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) + @provider.start + end + + it "should execute the stopsrc command" do + @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) + @provider.stop + end + + it "should execute status and return running if the subsystem is active" do + sample_output = <<_EOF_ +Subsystem Group PID Status +myservice tcpip 1234 active +_EOF_ + + @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output + @provider.status.should == :running + end + + it "should execute status and return stopped if the subsystem is inoperative" do + sample_output = <<_EOF_ +Subsystem Group PID Status +myservice tcpip inoperative +_EOF_ + + @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output + @provider.status.should == :stopped + end + + it "should execute status and return nil if the status is not known" do + sample_output = <<_EOF_ +Subsystem Group PID Status +myservice tcpip randomdata +_EOF_ + + @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output + @provider.status.should == nil + end + + it "should execute restart which runs refresh" do + sample_output = <<_EOF_ +#subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname: +myservice:::/usr/sbin/inetd:0:0:/dev/console:/dev/console:/dev/console:-O:-Q:-K:0:0:20:0:0:-d:20:tcpip: +_EOF_ + @provider.expects(:execute).with(['/usr/bin/lssrc', '-Ss', "myservice"]).returns sample_output + @provider.expects(:execute).with(['/usr/bin/refresh', '-s', "myservice"]) + @provider.restart + end + + it "should execute restart which runs stopsrc then startsrc" do + sample_output = <<_EOF_ +#subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname: +myservice::--no-daemonize:/usr/sbin/puppetd:0:0:/dev/null:/var/log/puppet.log:/var/log/puppet.log:-O:-Q:-S:0:0:20:15:9:-d:20::" +_EOF_ + @provider.expects(:execute).with(['/usr/bin/lssrc', '-Ss', "myservice"]).returns sample_output + @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) + @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) + @provider.restart + end +end diff --git a/spec/unit/provider/service/src_spec_spec.rb b/spec/unit/provider/service/src_spec_spec.rb deleted file mode 100755 index 76a6cf850..000000000 --- a/spec/unit/provider/service/src_spec_spec.rb +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env ruby -# -# Unit testing for the AIX System Resource Controller (src) provider -# - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:service).provider(:src) - -describe provider_class do - - before :each do - @resource = stub 'resource' - @resource.stubs(:[]).returns(nil) - @resource.stubs(:[]).with(:name).returns "myservice" - - @provider = provider_class.new - @provider.resource = @resource - - @provider.stubs(:command).with(:stopsrc).returns "/usr/bin/stopsrc" - @provider.stubs(:command).with(:startsrc).returns "/usr/bin/startsrc" - @provider.stubs(:command).with(:lssrc).returns "/usr/bin/lssrc" - @provider.stubs(:command).with(:refresh).returns "/usr/bin/refresh" - - @provider.stubs(:stopsrc) - @provider.stubs(:startsrc) - @provider.stubs(:lssrc) - @provider.stubs(:refresh) - end - - [:start, :stop, :status, :restart].each do |method| - it "should have a #{method} method" do - @provider.should respond_to(method) - end - end - - it "should execute the startsrc command" do - @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) - @provider.start - end - - it "should execute the stopsrc command" do - @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) - @provider.stop - end - - it "should execute status and return running if the subsystem is active" do - sample_output = <<_EOF_ -Subsystem Group PID Status -myservice tcpip 1234 active -_EOF_ - - @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output - @provider.status.should == :running - end - - it "should execute status and return stopped if the subsystem is inoperative" do - sample_output = <<_EOF_ -Subsystem Group PID Status -myservice tcpip inoperative -_EOF_ - - @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output - @provider.status.should == :stopped - end - - it "should execute status and return nil if the status is not known" do - sample_output = <<_EOF_ -Subsystem Group PID Status -myservice tcpip randomdata -_EOF_ - - @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output - @provider.status.should == nil - end - - it "should execute restart which runs refresh" do - sample_output = <<_EOF_ -#subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname: -myservice:::/usr/sbin/inetd:0:0:/dev/console:/dev/console:/dev/console:-O:-Q:-K:0:0:20:0:0:-d:20:tcpip: -_EOF_ - @provider.expects(:execute).with(['/usr/bin/lssrc', '-Ss', "myservice"]).returns sample_output - @provider.expects(:execute).with(['/usr/bin/refresh', '-s', "myservice"]) - @provider.restart - end - - it "should execute restart which runs stopsrc then startsrc" do - sample_output = <<_EOF_ -#subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname: -myservice::--no-daemonize:/usr/sbin/puppetd:0:0:/dev/null:/var/log/puppet.log:/var/log/puppet.log:-O:-Q:-S:0:0:20:15:9:-d:20::" -_EOF_ - @provider.expects(:execute).with(['/usr/bin/lssrc', '-Ss', "myservice"]).returns sample_output - @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) - @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) - @provider.restart - end -end diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb new file mode 100755 index 000000000..9fdd7f499 --- /dev/null +++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb @@ -0,0 +1,222 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet_spec/files' +require 'puppettest' +require 'puppettest/support/utils' +require 'puppettest/fileparsing' + +provider_class = Puppet::Type.type(:ssh_authorized_key).provider(:parsed) + +describe provider_class do + include PuppetSpec::Files + include PuppetTest + include PuppetTest::FileParsing + + before :each do + @sshauthkey_class = Puppet::Type.type(:ssh_authorized_key) + @provider = @sshauthkey_class.provider(:parsed) + @keyfile = File.join(Dir.tmpdir, 'authorized_keys') + @provider.any_instance.stubs(:target).returns @keyfile + @user = 'random_bob' + Puppet::Util.stubs(:uid).with(@user).returns 12345 + end + + after :each do + @provider.initvars + end + + def mkkey(args) + fakeresource = fakeresource(:ssh_authorized_key, args[:name]) + fakeresource.stubs(:should).with(:user).returns @user + fakeresource.stubs(:should).with(:target).returns @keyfile + + key = @provider.new(fakeresource) + args.each do |p,v| + key.send(p.to_s + "=", v) + end + + key + end + + def genkey(key) + @provider.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) + File.stubs(:chown) + File.stubs(:chmod) + Puppet::Util::SUIDManager.stubs(:asuser).yields + key.flush + @provider.target_object(@keyfile).read + end + + PuppetTest.fakedata("data/providers/ssh_authorized_key/parsed").each { |file| + it "should be able to parse example data in #{file}" do + fakedataparse(file) + end + } + + it "should be able to generate a basic authorized_keys file" do + key = mkkey({ + :name => "Just Testing", + :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj", + :type => "ssh-dss", + :ensure => :present, + :options => [:absent] + }) + + genkey(key).should == "ssh-dss AAAAfsfddsjldjgksdflgkjsfdlgkj Just Testing\n" + end + + it "should be able to generate a authorized_keys file with options" do + key = mkkey({ + :name => "root@localhost", + :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj", + :type => "ssh-rsa", + :ensure => :present, + :options => ['from="192.168.1.1"', "no-pty", "no-X11-forwarding"] + }) + + genkey(key).should == "from=\"192.168.1.1\",no-pty,no-X11-forwarding ssh-rsa AAAAfsfddsjldjgksdflgkjsfdlgkj root@localhost\n" + end + + it "should be able to parse options containing commas via its parse_options method" do + options = %w{from="host1.reductlivelabs.com,host.reductivelabs.com" command="/usr/local/bin/run" ssh-pty} + optionstr = options.join(", ") + + @provider.parse_options(optionstr).should == options + end + + it "should use '' as name for entries that lack a comment" do + line = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAut8aOSxenjOqF527dlsdHWV4MNoAsX14l9M297+SQXaQ5Z3BedIxZaoQthkDALlV/25A1COELrg9J2MqJNQc8Xe9XQOIkBQWWinUlD/BXwoOTWEy8C8zSZPHZ3getMMNhGTBO+q/O+qiJx3y5cA4MTbw2zSxukfWC87qWwcZ64UUlegIM056vPsdZWFclS9hsROVEa57YUMrehQ1EGxT4Z5j6zIopufGFiAPjZigq/vqgcAqhAKP6yu4/gwO6S9tatBeEjZ8fafvj1pmvvIplZeMr96gHE7xS3pEEQqnB3nd4RY7AF6j9kFixnsytAUO7STPh/M3pLiVQBN89TvWPQ==" + + @provider.parse(line)[0][:name].should == "" + end +end + +describe provider_class do + before :each do + @resource = stub("resource", :name => "foo") + @resource.stubs(:[]).returns "foo" + + @provider = provider_class.new(@resource) + provider_class.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) + Puppet::Util::SUIDManager.stubs(:asuser).yields + end + + describe "when flushing" do + before :each do + # Stub file and directory operations + Dir.stubs(:mkdir) + File.stubs(:chmod) + File.stubs(:chown) + end + + describe "and both a user and a target have been specified" do + before :each do + Puppet::Util.stubs(:uid).with("random_bob").returns 12345 + @resource.stubs(:should).with(:user).returns "random_bob" + target = "/tmp/.ssh_dir/place_to_put_authorized_keys" + @resource.stubs(:should).with(:target).returns target + end + + it "should create the directory" do + File.stubs(:exist?).with("/tmp/.ssh_dir").returns false + Dir.expects(:mkdir).with("/tmp/.ssh_dir", 0700) + @provider.flush + end + + it "should chown the directory to the user" do + uid = Puppet::Util.uid("random_bob") + File.expects(:chown).with(uid, nil, "/tmp/.ssh_dir") + @provider.flush + end + + it "should chown the key file to the user" do + uid = Puppet::Util.uid("random_bob") + File.expects(:chown).with(uid, nil, "/tmp/.ssh_dir/place_to_put_authorized_keys") + @provider.flush + end + + it "should chmod the key file to 0600" do + File.expects(:chmod).with(0600, "/tmp/.ssh_dir/place_to_put_authorized_keys") + @provider.flush + end + end + + describe "and a user has been specified with no target" do + before :each do + @resource.stubs(:should).with(:user).returns "nobody" + @resource.stubs(:should).with(:target).returns nil + # + # I'd like to use random_bob here and something like + # + # File.stubs(:expand_path).with("~random_bob/.ssh").returns "/users/r/random_bob/.ssh" + # + # but mocha objects strenuously to stubbing File.expand_path + # so I'm left with using nobody. + @dir = File.expand_path("~nobody/.ssh") + end + + it "should create the directory if it doesn't exist" do + File.stubs(:exist?).with(@dir).returns false + Dir.expects(:mkdir).with(@dir,0700) + @provider.flush + end + + it "should not create or chown the directory if it already exist" do + File.stubs(:exist?).with(@dir).returns false + Dir.expects(:mkdir).never + @provider.flush + end + + it "should chown the directory to the user if it creates it" do + File.stubs(:exist?).with(@dir).returns false + Dir.stubs(:mkdir).with(@dir,0700) + uid = Puppet::Util.uid("nobody") + File.expects(:chown).with(uid, nil, @dir) + @provider.flush + end + + it "should not create or chown the directory if it already exist" do + File.stubs(:exist?).with(@dir).returns false + Dir.expects(:mkdir).never + File.expects(:chown).never + @provider.flush + end + + it "should chown the key file to the user" do + uid = Puppet::Util.uid("nobody") + File.expects(:chown).with(uid, nil, File.expand_path("~nobody/.ssh/authorized_keys")) + @provider.flush + end + + it "should chmod the key file to 0600" do + File.expects(:chmod).with(0600, File.expand_path("~nobody/.ssh/authorized_keys")) + @provider.flush + end + end + + describe "and a target has been specified with no user" do + before :each do + @resource.stubs(:should).with(:user).returns nil + @resource.stubs(:should).with(:target).returns("/tmp/.ssh_dir/place_to_put_authorized_keys") + end + + it "should raise an error" do + proc { @provider.flush }.should raise_error + end + end + + describe "and a invalid user has been specified with no target" do + before :each do + @resource.stubs(:should).with(:user).returns "thisusershouldnotexist" + @resource.stubs(:should).with(:target).returns nil + end + + it "should catch an exception and raise a Puppet error" do + lambda { @provider.flush }.should raise_error(Puppet::Error) + end + end + + end +end diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec_spec.rb deleted file mode 100755 index 9fdd7f499..000000000 --- a/spec/unit/provider/ssh_authorized_key/parsed_spec_spec.rb +++ /dev/null @@ -1,222 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet_spec/files' -require 'puppettest' -require 'puppettest/support/utils' -require 'puppettest/fileparsing' - -provider_class = Puppet::Type.type(:ssh_authorized_key).provider(:parsed) - -describe provider_class do - include PuppetSpec::Files - include PuppetTest - include PuppetTest::FileParsing - - before :each do - @sshauthkey_class = Puppet::Type.type(:ssh_authorized_key) - @provider = @sshauthkey_class.provider(:parsed) - @keyfile = File.join(Dir.tmpdir, 'authorized_keys') - @provider.any_instance.stubs(:target).returns @keyfile - @user = 'random_bob' - Puppet::Util.stubs(:uid).with(@user).returns 12345 - end - - after :each do - @provider.initvars - end - - def mkkey(args) - fakeresource = fakeresource(:ssh_authorized_key, args[:name]) - fakeresource.stubs(:should).with(:user).returns @user - fakeresource.stubs(:should).with(:target).returns @keyfile - - key = @provider.new(fakeresource) - args.each do |p,v| - key.send(p.to_s + "=", v) - end - - key - end - - def genkey(key) - @provider.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) - File.stubs(:chown) - File.stubs(:chmod) - Puppet::Util::SUIDManager.stubs(:asuser).yields - key.flush - @provider.target_object(@keyfile).read - end - - PuppetTest.fakedata("data/providers/ssh_authorized_key/parsed").each { |file| - it "should be able to parse example data in #{file}" do - fakedataparse(file) - end - } - - it "should be able to generate a basic authorized_keys file" do - key = mkkey({ - :name => "Just Testing", - :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj", - :type => "ssh-dss", - :ensure => :present, - :options => [:absent] - }) - - genkey(key).should == "ssh-dss AAAAfsfddsjldjgksdflgkjsfdlgkj Just Testing\n" - end - - it "should be able to generate a authorized_keys file with options" do - key = mkkey({ - :name => "root@localhost", - :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj", - :type => "ssh-rsa", - :ensure => :present, - :options => ['from="192.168.1.1"', "no-pty", "no-X11-forwarding"] - }) - - genkey(key).should == "from=\"192.168.1.1\",no-pty,no-X11-forwarding ssh-rsa AAAAfsfddsjldjgksdflgkjsfdlgkj root@localhost\n" - end - - it "should be able to parse options containing commas via its parse_options method" do - options = %w{from="host1.reductlivelabs.com,host.reductivelabs.com" command="/usr/local/bin/run" ssh-pty} - optionstr = options.join(", ") - - @provider.parse_options(optionstr).should == options - end - - it "should use '' as name for entries that lack a comment" do - line = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAut8aOSxenjOqF527dlsdHWV4MNoAsX14l9M297+SQXaQ5Z3BedIxZaoQthkDALlV/25A1COELrg9J2MqJNQc8Xe9XQOIkBQWWinUlD/BXwoOTWEy8C8zSZPHZ3getMMNhGTBO+q/O+qiJx3y5cA4MTbw2zSxukfWC87qWwcZ64UUlegIM056vPsdZWFclS9hsROVEa57YUMrehQ1EGxT4Z5j6zIopufGFiAPjZigq/vqgcAqhAKP6yu4/gwO6S9tatBeEjZ8fafvj1pmvvIplZeMr96gHE7xS3pEEQqnB3nd4RY7AF6j9kFixnsytAUO7STPh/M3pLiVQBN89TvWPQ==" - - @provider.parse(line)[0][:name].should == "" - end -end - -describe provider_class do - before :each do - @resource = stub("resource", :name => "foo") - @resource.stubs(:[]).returns "foo" - - @provider = provider_class.new(@resource) - provider_class.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) - Puppet::Util::SUIDManager.stubs(:asuser).yields - end - - describe "when flushing" do - before :each do - # Stub file and directory operations - Dir.stubs(:mkdir) - File.stubs(:chmod) - File.stubs(:chown) - end - - describe "and both a user and a target have been specified" do - before :each do - Puppet::Util.stubs(:uid).with("random_bob").returns 12345 - @resource.stubs(:should).with(:user).returns "random_bob" - target = "/tmp/.ssh_dir/place_to_put_authorized_keys" - @resource.stubs(:should).with(:target).returns target - end - - it "should create the directory" do - File.stubs(:exist?).with("/tmp/.ssh_dir").returns false - Dir.expects(:mkdir).with("/tmp/.ssh_dir", 0700) - @provider.flush - end - - it "should chown the directory to the user" do - uid = Puppet::Util.uid("random_bob") - File.expects(:chown).with(uid, nil, "/tmp/.ssh_dir") - @provider.flush - end - - it "should chown the key file to the user" do - uid = Puppet::Util.uid("random_bob") - File.expects(:chown).with(uid, nil, "/tmp/.ssh_dir/place_to_put_authorized_keys") - @provider.flush - end - - it "should chmod the key file to 0600" do - File.expects(:chmod).with(0600, "/tmp/.ssh_dir/place_to_put_authorized_keys") - @provider.flush - end - end - - describe "and a user has been specified with no target" do - before :each do - @resource.stubs(:should).with(:user).returns "nobody" - @resource.stubs(:should).with(:target).returns nil - # - # I'd like to use random_bob here and something like - # - # File.stubs(:expand_path).with("~random_bob/.ssh").returns "/users/r/random_bob/.ssh" - # - # but mocha objects strenuously to stubbing File.expand_path - # so I'm left with using nobody. - @dir = File.expand_path("~nobody/.ssh") - end - - it "should create the directory if it doesn't exist" do - File.stubs(:exist?).with(@dir).returns false - Dir.expects(:mkdir).with(@dir,0700) - @provider.flush - end - - it "should not create or chown the directory if it already exist" do - File.stubs(:exist?).with(@dir).returns false - Dir.expects(:mkdir).never - @provider.flush - end - - it "should chown the directory to the user if it creates it" do - File.stubs(:exist?).with(@dir).returns false - Dir.stubs(:mkdir).with(@dir,0700) - uid = Puppet::Util.uid("nobody") - File.expects(:chown).with(uid, nil, @dir) - @provider.flush - end - - it "should not create or chown the directory if it already exist" do - File.stubs(:exist?).with(@dir).returns false - Dir.expects(:mkdir).never - File.expects(:chown).never - @provider.flush - end - - it "should chown the key file to the user" do - uid = Puppet::Util.uid("nobody") - File.expects(:chown).with(uid, nil, File.expand_path("~nobody/.ssh/authorized_keys")) - @provider.flush - end - - it "should chmod the key file to 0600" do - File.expects(:chmod).with(0600, File.expand_path("~nobody/.ssh/authorized_keys")) - @provider.flush - end - end - - describe "and a target has been specified with no user" do - before :each do - @resource.stubs(:should).with(:user).returns nil - @resource.stubs(:should).with(:target).returns("/tmp/.ssh_dir/place_to_put_authorized_keys") - end - - it "should raise an error" do - proc { @provider.flush }.should raise_error - end - end - - describe "and a invalid user has been specified with no target" do - before :each do - @resource.stubs(:should).with(:user).returns "thisusershouldnotexist" - @resource.stubs(:should).with(:target).returns nil - end - - it "should catch an exception and raise a Puppet error" do - lambda { @provider.flush }.should raise_error(Puppet::Error) - end - end - - end -end diff --git a/spec/unit/provider/sshkey/parsed_spec.rb b/spec/unit/provider/sshkey/parsed_spec.rb new file mode 100755 index 000000000..fc6c85dbd --- /dev/null +++ b/spec/unit/provider/sshkey/parsed_spec.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:sshkey).provider(:parsed) + +describe provider_class do + before do + @sshkey_class = Puppet::Type.type(:sshkey) + @provider_class = @sshkey_class.provider(:parsed) + @key = 'AAAAB3NzaC1yc2EAAAABIwAAAQEAzwHhxXvIrtfIwrudFqc8yQcIfMudrgpnuh1F3AV6d2BrLgu/yQE7W5UyJMUjfj427sQudRwKW45O0Jsnr33F4mUw+GIMlAAmp9g24/OcrTiB8ZUKIjoPy/cO4coxGi8/NECtRzpD/ZUPFh6OEpyOwJPMb7/EC2Az6Otw4StHdXUYw22zHazBcPFnv6zCgPx1hA7QlQDWTu4YcL0WmTYQCtMUb3FUqrcFtzGDD0ytosgwSd+JyN5vj5UwIABjnNOHPZ62EY1OFixnfqX/+dUwrFSs5tPgBF/KkC6R7tmbUfnBON6RrGEmu+ajOTOLy23qUZB4CQ53V7nyAWhzqSK+hw==' + end + + it "should parse the name from the first field" do + @provider_class.parse_line('test ssh-rsa '+@key)[:name].should == "test" + end + + it "should parse the first component of the first field as the name" do + @provider_class.parse_line('test,alias ssh-rsa '+@key)[:name].should == "test" + end + + it "should parse host_aliases from the remaining components of the first field" do + @provider_class.parse_line('test,alias ssh-rsa '+@key)[:host_aliases].should == ["alias"] + end + + it "should parse multiple host_aliases" do + @provider_class.parse_line('test,alias1,alias2,alias3 ssh-rsa '+@key)[:host_aliases].should == ["alias1","alias2","alias3"] + end + + it "should not drop an empty host_alias" do + @provider_class.parse_line('test,alias, ssh-rsa '+@key)[:host_aliases].should == ["alias",""] + end + + it "should recognise when there are no host aliases" do + @provider_class.parse_line('test ssh-rsa '+@key)[:host_aliases].should == [] + end + +end diff --git a/spec/unit/provider/sshkey/parsed_spec_spec.rb b/spec/unit/provider/sshkey/parsed_spec_spec.rb deleted file mode 100755 index fc6c85dbd..000000000 --- a/spec/unit/provider/sshkey/parsed_spec_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:sshkey).provider(:parsed) - -describe provider_class do - before do - @sshkey_class = Puppet::Type.type(:sshkey) - @provider_class = @sshkey_class.provider(:parsed) - @key = 'AAAAB3NzaC1yc2EAAAABIwAAAQEAzwHhxXvIrtfIwrudFqc8yQcIfMudrgpnuh1F3AV6d2BrLgu/yQE7W5UyJMUjfj427sQudRwKW45O0Jsnr33F4mUw+GIMlAAmp9g24/OcrTiB8ZUKIjoPy/cO4coxGi8/NECtRzpD/ZUPFh6OEpyOwJPMb7/EC2Az6Otw4StHdXUYw22zHazBcPFnv6zCgPx1hA7QlQDWTu4YcL0WmTYQCtMUb3FUqrcFtzGDD0ytosgwSd+JyN5vj5UwIABjnNOHPZ62EY1OFixnfqX/+dUwrFSs5tPgBF/KkC6R7tmbUfnBON6RrGEmu+ajOTOLy23qUZB4CQ53V7nyAWhzqSK+hw==' - end - - it "should parse the name from the first field" do - @provider_class.parse_line('test ssh-rsa '+@key)[:name].should == "test" - end - - it "should parse the first component of the first field as the name" do - @provider_class.parse_line('test,alias ssh-rsa '+@key)[:name].should == "test" - end - - it "should parse host_aliases from the remaining components of the first field" do - @provider_class.parse_line('test,alias ssh-rsa '+@key)[:host_aliases].should == ["alias"] - end - - it "should parse multiple host_aliases" do - @provider_class.parse_line('test,alias1,alias2,alias3 ssh-rsa '+@key)[:host_aliases].should == ["alias1","alias2","alias3"] - end - - it "should not drop an empty host_alias" do - @provider_class.parse_line('test,alias, ssh-rsa '+@key)[:host_aliases].should == ["alias",""] - end - - it "should recognise when there are no host aliases" do - @provider_class.parse_line('test ssh-rsa '+@key)[:host_aliases].should == [] - end - -end diff --git a/spec/unit/provider/user/hpux_spec.rb b/spec/unit/provider/user/hpux_spec.rb new file mode 100755 index 000000000..4129a7ab6 --- /dev/null +++ b/spec/unit/provider/user/hpux_spec.rb @@ -0,0 +1,25 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:user).provider(:hpuxuseradd) + +describe provider_class do + # left from the useradd test... I have no clue what I'm doing. + before do + @resource = stub("resource", :name => "myuser", :managehome? => nil, :should => "fakeval", :[] => "fakeval") + @provider = provider_class.new(@resource) + end + + it "should add -F when modifying a user" do + @resource.expects(:allowdupe?).returns true + @provider.expects(:execute).with { |args| args.include?("-F") } + @provider.uid = 1000 + end + + it "should add -F when deleting a user" do + @provider.stubs(:exists?).returns(true) + @provider.expects(:execute).with { |args| args.include?("-F") } + @provider.delete + end +end diff --git a/spec/unit/provider/user/hpux_spec_spec.rb b/spec/unit/provider/user/hpux_spec_spec.rb deleted file mode 100755 index 4129a7ab6..000000000 --- a/spec/unit/provider/user/hpux_spec_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:user).provider(:hpuxuseradd) - -describe provider_class do - # left from the useradd test... I have no clue what I'm doing. - before do - @resource = stub("resource", :name => "myuser", :managehome? => nil, :should => "fakeval", :[] => "fakeval") - @provider = provider_class.new(@resource) - end - - it "should add -F when modifying a user" do - @resource.expects(:allowdupe?).returns true - @provider.expects(:execute).with { |args| args.include?("-F") } - @provider.uid = 1000 - end - - it "should add -F when deleting a user" do - @provider.stubs(:exists?).returns(true) - @provider.expects(:execute).with { |args| args.include?("-F") } - @provider.delete - end -end diff --git a/spec/unit/provider/user/ldap_spec.rb b/spec/unit/provider/user/ldap_spec.rb new file mode 100755 index 000000000..adc2180ff --- /dev/null +++ b/spec/unit/provider/user/ldap_spec.rb @@ -0,0 +1,279 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-10. +# Copyright (c) 2006. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:user).provider(:ldap) + +describe provider_class do + it "should have the Ldap provider class as its baseclass" do + provider_class.superclass.should equal(Puppet::Provider::Ldap) + end + + it "should manage :posixAccount and :person objectclasses" do + provider_class.manager.objectclasses.should == [:posixAccount, :person] + end + + it "should use 'ou=People' as its relative base" do + provider_class.manager.location.should == "ou=People" + end + + it "should use :uid as its rdn" do + provider_class.manager.rdn.should == :uid + end + + it "should be able to manage passwords" do + provider_class.should be_manages_passwords + end + + it "should use the ldap group provider to convert group names to numbers" do + provider = provider_class.new(:name => "foo") + Puppet::Type.type(:group).provider(:ldap).expects(:name2id).with("bar").returns 10 + + provider.gid = 'bar' + provider.gid.should == 10 + end + + {:name => "uid", + :password => "userPassword", + :comment => "cn", + :uid => "uidNumber", + :gid => "gidNumber", + :home => "homeDirectory", + :shell => "loginShell" + }.each do |puppet, ldap| + it "should map :#{puppet.to_s} to '#{ldap}'" do + provider_class.manager.ldap_name(puppet).should == ldap + end + end + + describe "when being created" do + before do + # So we don't try to actually talk to ldap + @connection = mock 'connection' + provider_class.manager.stubs(:connect).yields @connection + end + + it "should generate the sn as the last field of the cn" do + resource = stub 'resource', :should => %w{whatever} + resource.stubs(:should).with(:comment).returns ["Luke Kanies"] + resource.stubs(:should).with(:ensure).returns :present + instance = provider_class.new(:name => "luke", :ensure => :absent) + instance.stubs(:resource).returns resource + + @connection.expects(:add).with { |dn, attrs| attrs["sn"] == ["Kanies"] } + + instance.create + instance.flush + end + + describe "with no uid specified" do + it "should pick the first available UID after the largest existing UID" do + low = {:name=>["luke"], :shell=>:absent, :uid=>["600"], :home=>["/h"], :gid=>["1000"], :password=>["blah"], :comment=>["l k"]} + high = {:name=>["testing"], :shell=>:absent, :uid=>["640"], :home=>["/h"], :gid=>["1000"], :password=>["blah"], :comment=>["t u"]} + provider_class.manager.expects(:search).returns([low, high]) + + resource = stub 'resource', :should => %w{whatever} + resource.stubs(:should).with(:uid).returns nil + resource.stubs(:should).with(:ensure).returns :present + instance = provider_class.new(:name => "luke", :ensure => :absent) + instance.stubs(:resource).returns resource + + @connection.expects(:add).with { |dn, attrs| attrs["uidNumber"] == ["641"] } + + instance.create + instance.flush + end + + it "should pick 501 of no users exist" do + provider_class.manager.expects(:search).returns nil + + resource = stub 'resource', :should => %w{whatever} + resource.stubs(:should).with(:uid).returns nil + resource.stubs(:should).with(:ensure).returns :present + instance = provider_class.new(:name => "luke", :ensure => :absent) + instance.stubs(:resource).returns resource + + @connection.expects(:add).with { |dn, attrs| attrs["uidNumber"] == ["501"] } + + instance.create + instance.flush + end + end + end + + describe "when flushing" do + before do + provider_class.stubs(:suitable?).returns true + + @instance = provider_class.new(:name => "myname", :groups => %w{whatever}, :uid => "400") + end + + it "should remove the :groups value before updating" do + @instance.class.manager.expects(:update).with { |name, ldap, puppet| puppet[:groups].nil? } + + @instance.flush + end + + it "should empty the property hash" do + @instance.class.manager.stubs(:update) + + @instance.flush + + @instance.uid.should == :absent + end + + it "should empty the ldap property hash" do + @instance.class.manager.stubs(:update) + + @instance.flush + + @instance.ldap_properties[:uid].should be_nil + end + end + + describe "when checking group membership" do + before do + @groups = Puppet::Type.type(:group).provider(:ldap) + @group_manager = @groups.manager + provider_class.stubs(:suitable?).returns true + + @instance = provider_class.new(:name => "myname") + end + + it "should show its group membership as the sorted list of all groups returned by an ldap query of group memberships" do + one = {:name => "one"} + two = {:name => "two"} + @group_manager.expects(:search).with("memberUid=myname").returns([two, one]) + + @instance.groups.should == "one,two" + end + + it "should show its group membership as :absent if no matching groups are found in ldap" do + @group_manager.expects(:search).with("memberUid=myname").returns(nil) + + @instance.groups.should == :absent + end + + it "should cache the group value" do + @group_manager.expects(:search).with("memberUid=myname").once.returns nil + + @instance.groups + @instance.groups.should == :absent + end + end + + describe "when modifying group membership" do + before do + @groups = Puppet::Type.type(:group).provider(:ldap) + @group_manager = @groups.manager + provider_class.stubs(:suitable?).returns true + + @one = {:name => "one", :gid => "500"} + @group_manager.stubs(:find).with("one").returns(@one) + + @two = {:name => "one", :gid => "600"} + @group_manager.stubs(:find).with("two").returns(@two) + + @instance = provider_class.new(:name => "myname") + + @instance.stubs(:groups).returns :absent + end + + it "should fail if the group does not exist" do + @group_manager.expects(:find).with("mygroup").returns nil + + lambda { @instance.groups = "mygroup" }.should raise_error(Puppet::Error) + end + + it "should only pass the attributes it cares about to the group manager" do + @group_manager.expects(:update).with { |name, attrs| attrs[:gid].nil? } + + @instance.groups = "one" + end + + it "should always include :ensure => :present in the current values" do + @group_manager.expects(:update).with { |name, is, should| is[:ensure] == :present } + + @instance.groups = "one" + end + + it "should always include :ensure => :present in the desired values" do + @group_manager.expects(:update).with { |name, is, should| should[:ensure] == :present } + + @instance.groups = "one" + end + + it "should always pass the group's original member list" do + @one[:members] = %w{yay ness} + @group_manager.expects(:update).with { |name, is, should| is[:members] == %w{yay ness} } + + @instance.groups = "one" + end + + it "should find the group again when resetting its member list, so it has the full member list" do + @group_manager.expects(:find).with("one").returns(@one) + + @group_manager.stubs(:update) + + @instance.groups = "one" + end + + describe "for groups that have no members" do + it "should create a new members attribute with its value being the user's name" do + @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{myname} } + + @instance.groups = "one" + end + end + + describe "for groups it is being removed from" do + it "should replace the group's member list with one missing the user's name" do + @one[:members] = %w{myname a} + @two[:members] = %w{myname b} + + @group_manager.expects(:update).with { |name, is, should| name == "two" and should[:members] == %w{b} } + + @instance.stubs(:groups).returns "one,two" + @instance.groups = "one" + end + + it "should mark the member list as empty if there are no remaining members" do + @one[:members] = %w{myname} + @two[:members] = %w{myname b} + + @group_manager.expects(:update).with { |name, is, should| name == "one" and should[:members] == :absent } + + @instance.stubs(:groups).returns "one,two" + @instance.groups = "two" + end + end + + describe "for groups that already have members" do + it "should replace each group's member list with a new list including the user's name" do + @one[:members] = %w{a b} + @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{a b myname} } + @two[:members] = %w{b c} + @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{b c myname} } + + @instance.groups = "one,two" + end + end + + describe "for groups of which it is a member" do + it "should do nothing" do + @one[:members] = %w{a b} + @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{a b myname} } + + @two[:members] = %w{c myname} + @group_manager.expects(:update).with { |name, *other| name == "two" }.never + + @instance.stubs(:groups).returns "two" + + @instance.groups = "one,two" + end + end + end +end diff --git a/spec/unit/provider/user/ldap_spec_spec.rb b/spec/unit/provider/user/ldap_spec_spec.rb deleted file mode 100755 index adc2180ff..000000000 --- a/spec/unit/provider/user/ldap_spec_spec.rb +++ /dev/null @@ -1,279 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-10. -# Copyright (c) 2006. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:user).provider(:ldap) - -describe provider_class do - it "should have the Ldap provider class as its baseclass" do - provider_class.superclass.should equal(Puppet::Provider::Ldap) - end - - it "should manage :posixAccount and :person objectclasses" do - provider_class.manager.objectclasses.should == [:posixAccount, :person] - end - - it "should use 'ou=People' as its relative base" do - provider_class.manager.location.should == "ou=People" - end - - it "should use :uid as its rdn" do - provider_class.manager.rdn.should == :uid - end - - it "should be able to manage passwords" do - provider_class.should be_manages_passwords - end - - it "should use the ldap group provider to convert group names to numbers" do - provider = provider_class.new(:name => "foo") - Puppet::Type.type(:group).provider(:ldap).expects(:name2id).with("bar").returns 10 - - provider.gid = 'bar' - provider.gid.should == 10 - end - - {:name => "uid", - :password => "userPassword", - :comment => "cn", - :uid => "uidNumber", - :gid => "gidNumber", - :home => "homeDirectory", - :shell => "loginShell" - }.each do |puppet, ldap| - it "should map :#{puppet.to_s} to '#{ldap}'" do - provider_class.manager.ldap_name(puppet).should == ldap - end - end - - describe "when being created" do - before do - # So we don't try to actually talk to ldap - @connection = mock 'connection' - provider_class.manager.stubs(:connect).yields @connection - end - - it "should generate the sn as the last field of the cn" do - resource = stub 'resource', :should => %w{whatever} - resource.stubs(:should).with(:comment).returns ["Luke Kanies"] - resource.stubs(:should).with(:ensure).returns :present - instance = provider_class.new(:name => "luke", :ensure => :absent) - instance.stubs(:resource).returns resource - - @connection.expects(:add).with { |dn, attrs| attrs["sn"] == ["Kanies"] } - - instance.create - instance.flush - end - - describe "with no uid specified" do - it "should pick the first available UID after the largest existing UID" do - low = {:name=>["luke"], :shell=>:absent, :uid=>["600"], :home=>["/h"], :gid=>["1000"], :password=>["blah"], :comment=>["l k"]} - high = {:name=>["testing"], :shell=>:absent, :uid=>["640"], :home=>["/h"], :gid=>["1000"], :password=>["blah"], :comment=>["t u"]} - provider_class.manager.expects(:search).returns([low, high]) - - resource = stub 'resource', :should => %w{whatever} - resource.stubs(:should).with(:uid).returns nil - resource.stubs(:should).with(:ensure).returns :present - instance = provider_class.new(:name => "luke", :ensure => :absent) - instance.stubs(:resource).returns resource - - @connection.expects(:add).with { |dn, attrs| attrs["uidNumber"] == ["641"] } - - instance.create - instance.flush - end - - it "should pick 501 of no users exist" do - provider_class.manager.expects(:search).returns nil - - resource = stub 'resource', :should => %w{whatever} - resource.stubs(:should).with(:uid).returns nil - resource.stubs(:should).with(:ensure).returns :present - instance = provider_class.new(:name => "luke", :ensure => :absent) - instance.stubs(:resource).returns resource - - @connection.expects(:add).with { |dn, attrs| attrs["uidNumber"] == ["501"] } - - instance.create - instance.flush - end - end - end - - describe "when flushing" do - before do - provider_class.stubs(:suitable?).returns true - - @instance = provider_class.new(:name => "myname", :groups => %w{whatever}, :uid => "400") - end - - it "should remove the :groups value before updating" do - @instance.class.manager.expects(:update).with { |name, ldap, puppet| puppet[:groups].nil? } - - @instance.flush - end - - it "should empty the property hash" do - @instance.class.manager.stubs(:update) - - @instance.flush - - @instance.uid.should == :absent - end - - it "should empty the ldap property hash" do - @instance.class.manager.stubs(:update) - - @instance.flush - - @instance.ldap_properties[:uid].should be_nil - end - end - - describe "when checking group membership" do - before do - @groups = Puppet::Type.type(:group).provider(:ldap) - @group_manager = @groups.manager - provider_class.stubs(:suitable?).returns true - - @instance = provider_class.new(:name => "myname") - end - - it "should show its group membership as the sorted list of all groups returned by an ldap query of group memberships" do - one = {:name => "one"} - two = {:name => "two"} - @group_manager.expects(:search).with("memberUid=myname").returns([two, one]) - - @instance.groups.should == "one,two" - end - - it "should show its group membership as :absent if no matching groups are found in ldap" do - @group_manager.expects(:search).with("memberUid=myname").returns(nil) - - @instance.groups.should == :absent - end - - it "should cache the group value" do - @group_manager.expects(:search).with("memberUid=myname").once.returns nil - - @instance.groups - @instance.groups.should == :absent - end - end - - describe "when modifying group membership" do - before do - @groups = Puppet::Type.type(:group).provider(:ldap) - @group_manager = @groups.manager - provider_class.stubs(:suitable?).returns true - - @one = {:name => "one", :gid => "500"} - @group_manager.stubs(:find).with("one").returns(@one) - - @two = {:name => "one", :gid => "600"} - @group_manager.stubs(:find).with("two").returns(@two) - - @instance = provider_class.new(:name => "myname") - - @instance.stubs(:groups).returns :absent - end - - it "should fail if the group does not exist" do - @group_manager.expects(:find).with("mygroup").returns nil - - lambda { @instance.groups = "mygroup" }.should raise_error(Puppet::Error) - end - - it "should only pass the attributes it cares about to the group manager" do - @group_manager.expects(:update).with { |name, attrs| attrs[:gid].nil? } - - @instance.groups = "one" - end - - it "should always include :ensure => :present in the current values" do - @group_manager.expects(:update).with { |name, is, should| is[:ensure] == :present } - - @instance.groups = "one" - end - - it "should always include :ensure => :present in the desired values" do - @group_manager.expects(:update).with { |name, is, should| should[:ensure] == :present } - - @instance.groups = "one" - end - - it "should always pass the group's original member list" do - @one[:members] = %w{yay ness} - @group_manager.expects(:update).with { |name, is, should| is[:members] == %w{yay ness} } - - @instance.groups = "one" - end - - it "should find the group again when resetting its member list, so it has the full member list" do - @group_manager.expects(:find).with("one").returns(@one) - - @group_manager.stubs(:update) - - @instance.groups = "one" - end - - describe "for groups that have no members" do - it "should create a new members attribute with its value being the user's name" do - @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{myname} } - - @instance.groups = "one" - end - end - - describe "for groups it is being removed from" do - it "should replace the group's member list with one missing the user's name" do - @one[:members] = %w{myname a} - @two[:members] = %w{myname b} - - @group_manager.expects(:update).with { |name, is, should| name == "two" and should[:members] == %w{b} } - - @instance.stubs(:groups).returns "one,two" - @instance.groups = "one" - end - - it "should mark the member list as empty if there are no remaining members" do - @one[:members] = %w{myname} - @two[:members] = %w{myname b} - - @group_manager.expects(:update).with { |name, is, should| name == "one" and should[:members] == :absent } - - @instance.stubs(:groups).returns "one,two" - @instance.groups = "two" - end - end - - describe "for groups that already have members" do - it "should replace each group's member list with a new list including the user's name" do - @one[:members] = %w{a b} - @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{a b myname} } - @two[:members] = %w{b c} - @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{b c myname} } - - @instance.groups = "one,two" - end - end - - describe "for groups of which it is a member" do - it "should do nothing" do - @one[:members] = %w{a b} - @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{a b myname} } - - @two[:members] = %w{c myname} - @group_manager.expects(:update).with { |name, *other| name == "two" }.never - - @instance.stubs(:groups).returns "two" - - @instance.groups = "one,two" - end - end - end -end diff --git a/spec/unit/provider/user/user_role_add_spec.rb b/spec/unit/provider/user/user_role_add_spec.rb new file mode 100644 index 000000000..781eba428 --- /dev/null +++ b/spec/unit/provider/user/user_role_add_spec.rb @@ -0,0 +1,249 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:user).provider(:user_role_add) + +describe provider_class do + before do + @resource = stub("resource", :name => "myuser", :managehome? => nil) + @resource.stubs(:should).returns "fakeval" + @resource.stubs(:[]).returns "fakeval" + @resource.stubs(:allowdupe?).returns false + @provider = provider_class.new(@resource) + end + + describe "when calling command" do + before do + klass = stub("provider") + klass.stubs(:command).with(:foo).returns("userfoo") + klass.stubs(:command).with(:role_foo).returns("rolefoo") + @provider.stubs(:class).returns(klass) + end + + it "should use the command if not a role and ensure!=role" do + @provider.stubs(:is_role?).returns(false) + @provider.stubs(:exists?).returns(false) + @resource.stubs(:[]).with(:ensure).returns(:present) + @provider.command(:foo).should == "userfoo" + end + + it "should use the role command when a role" do + @provider.stubs(:is_role?).returns(true) + @provider.command(:foo).should == "rolefoo" + end + + it "should use the role command when !exists and ensure=role" do + @provider.stubs(:is_role?).returns(false) + @provider.stubs(:exists?).returns(false) + @resource.stubs(:[]).with(:ensure).returns(:role) + @provider.command(:foo).should == "rolefoo" + end + end + + describe "when calling transition" do + it "should return the type set to whatever is passed in" do + @provider.expects(:command).with(:modify).returns("foomod") + @provider.transition("bar").include?("type=bar") + end + end + + describe "when calling create" do + before do + @provider.stubs(:password=) + end + + it "should use the add command when the user is not a role" do + @provider.stubs(:is_role?).returns(false) + @provider.expects(:addcmd).returns("useradd") + @provider.expects(:run) + @provider.create + end + + it "should use transition(normal) when the user is a role" do + @provider.stubs(:is_role?).returns(true) + @provider.expects(:transition).with("normal") + @provider.expects(:run) + @provider.create + end + end + + describe "when calling destroy" do + it "should use the delete command if the user exists and is not a role" do + @provider.stubs(:exists?).returns(true) + @provider.stubs(:is_role?).returns(false) + @provider.expects(:deletecmd) + @provider.expects(:run) + @provider.destroy + end + + it "should use the delete command if the user is a role" do + @provider.stubs(:exists?).returns(true) + @provider.stubs(:is_role?).returns(true) + @provider.expects(:deletecmd) + @provider.expects(:run) + @provider.destroy + end + end + + describe "when calling create_role" do + it "should use the transition(role) if the user exists" do + @provider.stubs(:exists?).returns(true) + @provider.stubs(:is_role?).returns(false) + @provider.expects(:transition).with("role") + @provider.expects(:run) + @provider.create_role + end + + it "should use the add command when role doesn't exists" do + @provider.stubs(:exists?).returns(false) + @provider.expects(:addcmd) + @provider.expects(:run) + @provider.create_role + end + end + + describe "when allow duplicate is enabled" do + before do + @resource.expects(:allowdupe?).returns true + @provider.stubs(:is_role?).returns(false) + @provider.expects(:execute).with { |args| args.include?("-o") } + end + + it "should add -o when the user is being created" do + @provider.stubs(:password=) + @provider.create + end + + it "should add -o when the uid is being modified" do + @provider.uid = 150 + end + end + + [:roles, :auths, :profiles].each do |val| + describe "when getting #{val}" do + it "should get the user_attributes" do + @provider.expects(:user_attributes) + @provider.send(val) + end + + it "should get the #{val} attribute" do + attributes = mock("attributes") + attributes.expects(:[]).with(val) + @provider.stubs(:user_attributes).returns(attributes) + @provider.send(val) + end + end + end + + describe "when getting the keys" do + it "should get the user_attributes" do + @provider.expects(:user_attributes) + @provider.keys + end + + it "should call removed_managed_attributes" do + @provider.stubs(:user_attributes).returns({ :type => "normal", :foo => "something" }) + @provider.expects(:remove_managed_attributes) + @provider.keys + end + + it "should removed managed attribute (type, auths, roles, etc)" do + @provider.stubs(:user_attributes).returns({ :type => "normal", :foo => "something" }) + @provider.keys.should == { :foo => "something" } + end + end + + describe "when adding properties" do + it "should call build_keys_cmd" do + @resource.stubs(:should).returns "" + @resource.expects(:should).with(:keys).returns({ :foo => "bar" }) + @provider.expects(:build_keys_cmd).returns([]) + @provider.add_properties + end + + it "should add the elements of the keys hash to an array" do + @resource.stubs(:should).returns "" + @resource.expects(:should).with(:keys).returns({ :foo => "bar"}) + @provider.add_properties.must == ["-K", "foo=bar"] + end + end + + describe "when calling build_keys_cmd" do + it "should build cmd array with keypairs seperated by -K ending with user" do + @provider.build_keys_cmd({"foo" => "bar", "baz" => "boo"}).should.eql? ["-K", "foo=bar", "-K", "baz=boo"] + end + end + + describe "when setting the keys" do + before do + @provider.stubs(:is_role?).returns(false) + end + + it "should run a command" do + @provider.expects(:run) + @provider.keys=({}) + end + + it "should build the command" do + @resource.stubs(:[]).with(:name).returns("someuser") + @provider.stubs(:command).returns("usermod") + @provider.expects(:build_keys_cmd).returns(["-K", "foo=bar"]) + @provider.expects(:run).with(["usermod", "-K", "foo=bar", "someuser"], "modify attribute key pairs") + @provider.keys=({}) + end + end + + describe "when getting the hashed password" do + before do + @array = mock "array" + end + + it "should readlines of /etc/shadow" do + File.expects(:readlines).with("/etc/shadow").returns([]) + @provider.password + end + + it "should reject anything that doesn't start with alpha numerics" do + @array.expects(:reject).returns([]) + File.stubs(:readlines).with("/etc/shadow").returns(@array) + @provider.password + end + + it "should collect splitting on ':'" do + @array.stubs(:reject).returns(@array) + @array.expects(:collect).returns([]) + File.stubs(:readlines).with("/etc/shadow").returns(@array) + @provider.password + end + + it "should find the matching user" do + @resource.stubs(:[]).with(:name).returns("username") + @array.stubs(:reject).returns(@array) + @array.stubs(:collect).returns([["username", "hashedpassword"], ["someoneelse", "theirpassword"]]) + File.stubs(:readlines).with("/etc/shadow").returns(@array) + @provider.password.must == "hashedpassword" + end + + it "should get the right password" do + @resource.stubs(:[]).with(:name).returns("username") + File.stubs(:readlines).with("/etc/shadow").returns(["#comment", " nonsense", " ", "username:hashedpassword:stuff:foo:bar:::", "other:pword:yay:::"]) + @provider.password.must == "hashedpassword" + end + end + + describe "when setting the password" do + #how can you mock these blocks up? + it "should open /etc/shadow for reading and /etc/shadow_tmp for writing" do + File.expects(:open).with("/etc/shadow", "r") + File.stubs(:rename) + @provider.password=("hashedpassword") + end + + it "should rename the /etc/shadow_tmp to /etc/shadow" do + File.stubs(:open).with("/etc/shadow", "r") + File.expects(:rename).with("/etc/shadow_tmp", "/etc/shadow") + @provider.password=("hashedpassword") + end + end +end diff --git a/spec/unit/provider/user/user_role_add_spec_spec.rb b/spec/unit/provider/user/user_role_add_spec_spec.rb deleted file mode 100644 index 781eba428..000000000 --- a/spec/unit/provider/user/user_role_add_spec_spec.rb +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:user).provider(:user_role_add) - -describe provider_class do - before do - @resource = stub("resource", :name => "myuser", :managehome? => nil) - @resource.stubs(:should).returns "fakeval" - @resource.stubs(:[]).returns "fakeval" - @resource.stubs(:allowdupe?).returns false - @provider = provider_class.new(@resource) - end - - describe "when calling command" do - before do - klass = stub("provider") - klass.stubs(:command).with(:foo).returns("userfoo") - klass.stubs(:command).with(:role_foo).returns("rolefoo") - @provider.stubs(:class).returns(klass) - end - - it "should use the command if not a role and ensure!=role" do - @provider.stubs(:is_role?).returns(false) - @provider.stubs(:exists?).returns(false) - @resource.stubs(:[]).with(:ensure).returns(:present) - @provider.command(:foo).should == "userfoo" - end - - it "should use the role command when a role" do - @provider.stubs(:is_role?).returns(true) - @provider.command(:foo).should == "rolefoo" - end - - it "should use the role command when !exists and ensure=role" do - @provider.stubs(:is_role?).returns(false) - @provider.stubs(:exists?).returns(false) - @resource.stubs(:[]).with(:ensure).returns(:role) - @provider.command(:foo).should == "rolefoo" - end - end - - describe "when calling transition" do - it "should return the type set to whatever is passed in" do - @provider.expects(:command).with(:modify).returns("foomod") - @provider.transition("bar").include?("type=bar") - end - end - - describe "when calling create" do - before do - @provider.stubs(:password=) - end - - it "should use the add command when the user is not a role" do - @provider.stubs(:is_role?).returns(false) - @provider.expects(:addcmd).returns("useradd") - @provider.expects(:run) - @provider.create - end - - it "should use transition(normal) when the user is a role" do - @provider.stubs(:is_role?).returns(true) - @provider.expects(:transition).with("normal") - @provider.expects(:run) - @provider.create - end - end - - describe "when calling destroy" do - it "should use the delete command if the user exists and is not a role" do - @provider.stubs(:exists?).returns(true) - @provider.stubs(:is_role?).returns(false) - @provider.expects(:deletecmd) - @provider.expects(:run) - @provider.destroy - end - - it "should use the delete command if the user is a role" do - @provider.stubs(:exists?).returns(true) - @provider.stubs(:is_role?).returns(true) - @provider.expects(:deletecmd) - @provider.expects(:run) - @provider.destroy - end - end - - describe "when calling create_role" do - it "should use the transition(role) if the user exists" do - @provider.stubs(:exists?).returns(true) - @provider.stubs(:is_role?).returns(false) - @provider.expects(:transition).with("role") - @provider.expects(:run) - @provider.create_role - end - - it "should use the add command when role doesn't exists" do - @provider.stubs(:exists?).returns(false) - @provider.expects(:addcmd) - @provider.expects(:run) - @provider.create_role - end - end - - describe "when allow duplicate is enabled" do - before do - @resource.expects(:allowdupe?).returns true - @provider.stubs(:is_role?).returns(false) - @provider.expects(:execute).with { |args| args.include?("-o") } - end - - it "should add -o when the user is being created" do - @provider.stubs(:password=) - @provider.create - end - - it "should add -o when the uid is being modified" do - @provider.uid = 150 - end - end - - [:roles, :auths, :profiles].each do |val| - describe "when getting #{val}" do - it "should get the user_attributes" do - @provider.expects(:user_attributes) - @provider.send(val) - end - - it "should get the #{val} attribute" do - attributes = mock("attributes") - attributes.expects(:[]).with(val) - @provider.stubs(:user_attributes).returns(attributes) - @provider.send(val) - end - end - end - - describe "when getting the keys" do - it "should get the user_attributes" do - @provider.expects(:user_attributes) - @provider.keys - end - - it "should call removed_managed_attributes" do - @provider.stubs(:user_attributes).returns({ :type => "normal", :foo => "something" }) - @provider.expects(:remove_managed_attributes) - @provider.keys - end - - it "should removed managed attribute (type, auths, roles, etc)" do - @provider.stubs(:user_attributes).returns({ :type => "normal", :foo => "something" }) - @provider.keys.should == { :foo => "something" } - end - end - - describe "when adding properties" do - it "should call build_keys_cmd" do - @resource.stubs(:should).returns "" - @resource.expects(:should).with(:keys).returns({ :foo => "bar" }) - @provider.expects(:build_keys_cmd).returns([]) - @provider.add_properties - end - - it "should add the elements of the keys hash to an array" do - @resource.stubs(:should).returns "" - @resource.expects(:should).with(:keys).returns({ :foo => "bar"}) - @provider.add_properties.must == ["-K", "foo=bar"] - end - end - - describe "when calling build_keys_cmd" do - it "should build cmd array with keypairs seperated by -K ending with user" do - @provider.build_keys_cmd({"foo" => "bar", "baz" => "boo"}).should.eql? ["-K", "foo=bar", "-K", "baz=boo"] - end - end - - describe "when setting the keys" do - before do - @provider.stubs(:is_role?).returns(false) - end - - it "should run a command" do - @provider.expects(:run) - @provider.keys=({}) - end - - it "should build the command" do - @resource.stubs(:[]).with(:name).returns("someuser") - @provider.stubs(:command).returns("usermod") - @provider.expects(:build_keys_cmd).returns(["-K", "foo=bar"]) - @provider.expects(:run).with(["usermod", "-K", "foo=bar", "someuser"], "modify attribute key pairs") - @provider.keys=({}) - end - end - - describe "when getting the hashed password" do - before do - @array = mock "array" - end - - it "should readlines of /etc/shadow" do - File.expects(:readlines).with("/etc/shadow").returns([]) - @provider.password - end - - it "should reject anything that doesn't start with alpha numerics" do - @array.expects(:reject).returns([]) - File.stubs(:readlines).with("/etc/shadow").returns(@array) - @provider.password - end - - it "should collect splitting on ':'" do - @array.stubs(:reject).returns(@array) - @array.expects(:collect).returns([]) - File.stubs(:readlines).with("/etc/shadow").returns(@array) - @provider.password - end - - it "should find the matching user" do - @resource.stubs(:[]).with(:name).returns("username") - @array.stubs(:reject).returns(@array) - @array.stubs(:collect).returns([["username", "hashedpassword"], ["someoneelse", "theirpassword"]]) - File.stubs(:readlines).with("/etc/shadow").returns(@array) - @provider.password.must == "hashedpassword" - end - - it "should get the right password" do - @resource.stubs(:[]).with(:name).returns("username") - File.stubs(:readlines).with("/etc/shadow").returns(["#comment", " nonsense", " ", "username:hashedpassword:stuff:foo:bar:::", "other:pword:yay:::"]) - @provider.password.must == "hashedpassword" - end - end - - describe "when setting the password" do - #how can you mock these blocks up? - it "should open /etc/shadow for reading and /etc/shadow_tmp for writing" do - File.expects(:open).with("/etc/shadow", "r") - File.stubs(:rename) - @provider.password=("hashedpassword") - end - - it "should rename the /etc/shadow_tmp to /etc/shadow" do - File.stubs(:open).with("/etc/shadow", "r") - File.expects(:rename).with("/etc/shadow_tmp", "/etc/shadow") - @provider.password=("hashedpassword") - end - end -end diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb new file mode 100755 index 000000000..463212e4e --- /dev/null +++ b/spec/unit/provider/user/useradd_spec.rb @@ -0,0 +1,115 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:user).provider(:useradd) + +describe provider_class do + before do + @resource = stub("resource", :name => "myuser", :managehome? => nil) + @resource.stubs(:should).returns "fakeval" + @resource.stubs(:[]).returns "fakeval" + @provider = provider_class.new(@resource) + end + + # #1360 + it "should add -o when allowdupe is enabled and the user is being created" do + @resource.expects(:allowdupe?).returns true + @provider.expects(:execute).with { |args| args.include?("-o") } + @provider.create + end + + it "should add -o when allowdupe is enabled and the uid is being modified" do + @resource.expects(:allowdupe?).returns true + @provider.expects(:execute).with { |args| args.include?("-o") } + + @provider.uid = 150 + end + + describe "when checking to add allow dup" do + it "should check allow dup" do + @resource.expects(:allowdupe?) + @provider.check_allow_dup + end + + it "should return an array with a flag if dup is allowed" do + @resource.stubs(:allowdupe?).returns true + @provider.check_allow_dup.must == ["-o"] + end + + it "should return an empty array if no dup is allowed" do + @resource.stubs(:allowdupe?).returns false + @provider.check_allow_dup.must == [] + end + end + + describe "when checking manage home" do + it "should check manage home" do + @resource.expects(:managehome?) + @provider.check_manage_home + end + + it "should return an array with -m flag if home is managed" do + @resource.stubs(:managehome?).returns true + @provider.check_manage_home.must == ["-m"] + end + + it "should return an array with -M if home is not managed and on Redhat" do + Facter.stubs(:value).with("operatingsystem").returns("RedHat") + @resource.stubs(:managehome?).returns false + @provider.check_manage_home.must == ["-M"] + end + + it "should return an empty array if home is not managed and not on Redhat" do + Facter.stubs(:value).with("operatingsystem").returns("some OS") + @resource.stubs(:managehome?).returns false + @provider.check_manage_home.must == [] + end + end + + describe "when adding properties" do + it "should get the valid properties" + it "should not add the ensure property" + it "should add the flag and value to an array" + it "should return and array of flags and values" + end + + describe "when calling addcmd" do + before do + @resource.stubs(:allowdupe?).returns true + @resource.stubs(:managehome?).returns true + end + + it "should call command with :add" do + @provider.expects(:command).with(:add) + @provider.addcmd + end + + it "should add properties" do + @provider.expects(:add_properties).returns([]) + @provider.addcmd + end + + it "should check and add if dup allowed" do + @provider.expects(:check_allow_dup).returns([]) + @provider.addcmd + end + + it "should check and add if home is managed" do + @provider.expects(:check_manage_home).returns([]) + @provider.addcmd + end + + it "should add the resource :name" do + @resource.expects(:[]).with(:name) + @provider.addcmd + end + + it "should return an array with full command" do + @provider.stubs(:command).with(:add).returns("useradd") + @provider.stubs(:add_properties).returns(["-G", "somegroup"]) + @resource.stubs(:[]).with(:name).returns("someuser") + @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", "someuser"] + end + end +end diff --git a/spec/unit/provider/user/useradd_spec_spec.rb b/spec/unit/provider/user/useradd_spec_spec.rb deleted file mode 100755 index 463212e4e..000000000 --- a/spec/unit/provider/user/useradd_spec_spec.rb +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:user).provider(:useradd) - -describe provider_class do - before do - @resource = stub("resource", :name => "myuser", :managehome? => nil) - @resource.stubs(:should).returns "fakeval" - @resource.stubs(:[]).returns "fakeval" - @provider = provider_class.new(@resource) - end - - # #1360 - it "should add -o when allowdupe is enabled and the user is being created" do - @resource.expects(:allowdupe?).returns true - @provider.expects(:execute).with { |args| args.include?("-o") } - @provider.create - end - - it "should add -o when allowdupe is enabled and the uid is being modified" do - @resource.expects(:allowdupe?).returns true - @provider.expects(:execute).with { |args| args.include?("-o") } - - @provider.uid = 150 - end - - describe "when checking to add allow dup" do - it "should check allow dup" do - @resource.expects(:allowdupe?) - @provider.check_allow_dup - end - - it "should return an array with a flag if dup is allowed" do - @resource.stubs(:allowdupe?).returns true - @provider.check_allow_dup.must == ["-o"] - end - - it "should return an empty array if no dup is allowed" do - @resource.stubs(:allowdupe?).returns false - @provider.check_allow_dup.must == [] - end - end - - describe "when checking manage home" do - it "should check manage home" do - @resource.expects(:managehome?) - @provider.check_manage_home - end - - it "should return an array with -m flag if home is managed" do - @resource.stubs(:managehome?).returns true - @provider.check_manage_home.must == ["-m"] - end - - it "should return an array with -M if home is not managed and on Redhat" do - Facter.stubs(:value).with("operatingsystem").returns("RedHat") - @resource.stubs(:managehome?).returns false - @provider.check_manage_home.must == ["-M"] - end - - it "should return an empty array if home is not managed and not on Redhat" do - Facter.stubs(:value).with("operatingsystem").returns("some OS") - @resource.stubs(:managehome?).returns false - @provider.check_manage_home.must == [] - end - end - - describe "when adding properties" do - it "should get the valid properties" - it "should not add the ensure property" - it "should add the flag and value to an array" - it "should return and array of flags and values" - end - - describe "when calling addcmd" do - before do - @resource.stubs(:allowdupe?).returns true - @resource.stubs(:managehome?).returns true - end - - it "should call command with :add" do - @provider.expects(:command).with(:add) - @provider.addcmd - end - - it "should add properties" do - @provider.expects(:add_properties).returns([]) - @provider.addcmd - end - - it "should check and add if dup allowed" do - @provider.expects(:check_allow_dup).returns([]) - @provider.addcmd - end - - it "should check and add if home is managed" do - @provider.expects(:check_manage_home).returns([]) - @provider.addcmd - end - - it "should add the resource :name" do - @resource.expects(:[]).with(:name) - @provider.addcmd - end - - it "should return an array with full command" do - @provider.stubs(:command).with(:add).returns("useradd") - @provider.stubs(:add_properties).returns(["-G", "somegroup"]) - @resource.stubs(:[]).with(:name).returns("someuser") - @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", "someuser"] - end - end -end diff --git a/spec/unit/provider/zfs/solaris_spec.rb b/spec/unit/provider/zfs/solaris_spec.rb new file mode 100755 index 000000000..8d1d8ccd6 --- /dev/null +++ b/spec/unit/provider/zfs/solaris_spec.rb @@ -0,0 +1,99 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:zfs).provider(:solaris) + +describe provider_class do + before do + @resource = stub("resource", :name => "myzfs") + @resource.stubs(:[]).with(:name).returns "myzfs" + @resource.stubs(:[]).returns "shouldvalue" + @provider = provider_class.new(@resource) + end + + it "should have a create method" do + @provider.should respond_to(:create) + end + + it "should have a destroy method" do + @provider.should respond_to(:destroy) + end + + it "should have an exists? method" do + @provider.should respond_to(:exists?) + end + + describe "when calling add_properties" do + it "should add -o and the key=value for each properties with a value" do + @resource.stubs(:[]).with(:quota).returns "" + @resource.stubs(:[]).with(:mountpoint).returns "/foo" + properties = @provider.add_properties + properties.include?("-o").should == true + properties.include?("mountpoint=/foo").should == true + properties.detect { |a| a.include?("quota") }.should == nil + end + end + + describe "when calling create" do + it "should call add_properties" do + @provider.stubs(:zfs) + @provider.expects(:add_properties).returns([]) + @provider.create + end + + it "should call zfs with create, properties and this zfs" do + @provider.stubs(:add_properties).returns(%w{a b}) + @provider.expects(:zfs).with(:create, "a", "b", @resource[:name]) + @provider.create + end + end + + describe "when calling destroy" do + it "should call zfs with :destroy and this zfs" do + @provider.expects(:zfs).with(:destroy, @resource[:name]) + @provider.destroy + end + end + + describe "when calling exist?" do + it "should call zfs with :list" do + #return stuff because we have to slice and dice it + @provider.expects(:zfs).with(:list).returns("NAME USED AVAIL REFER MOUNTPOINT\nmyzfs 100K 27.4M /myzfs") + @provider.exists? + end + + it "should return true if returned values match the name" do + @provider.stubs(:zfs).with(:list).returns("NAME USED AVAIL REFER MOUNTPOINT\n#{@resource[:name]} 100K 27.4M /myzfs") + @provider.exists?.should == true + end + + it "should return false if returned values don't match the name" do + @provider.stubs(:zfs).with(:list).returns("no soup for you") + @provider.exists?.should == false + end + + end + + [:mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |prop| + describe "when getting the #{prop} value" do + it "should call zfs with :get, #{prop} and this zfs" do + @provider.expects(:zfs).with(:get, "-H", "-o", "value", prop, @resource[:name]).returns("value\n") + @provider.send(prop) + end + + it "should get the third value of the second line from the output" do + @provider.stubs(:zfs).with(:get, "-H", "-o", "value", prop, @resource[:name]).returns("value\n") + @provider.send(prop).should == "value" + end + end + + describe "when setting the #{prop} value" do + it "should call zfs with :set, #{prop}=value and this zfs" do + @provider.expects(:zfs).with(:set, "#{prop}=value", @resource[:name]) + @provider.send("#{prop}=".intern, "value") + end + end + end + +end diff --git a/spec/unit/provider/zfs/solaris_spec_spec.rb b/spec/unit/provider/zfs/solaris_spec_spec.rb deleted file mode 100755 index 8d1d8ccd6..000000000 --- a/spec/unit/provider/zfs/solaris_spec_spec.rb +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:zfs).provider(:solaris) - -describe provider_class do - before do - @resource = stub("resource", :name => "myzfs") - @resource.stubs(:[]).with(:name).returns "myzfs" - @resource.stubs(:[]).returns "shouldvalue" - @provider = provider_class.new(@resource) - end - - it "should have a create method" do - @provider.should respond_to(:create) - end - - it "should have a destroy method" do - @provider.should respond_to(:destroy) - end - - it "should have an exists? method" do - @provider.should respond_to(:exists?) - end - - describe "when calling add_properties" do - it "should add -o and the key=value for each properties with a value" do - @resource.stubs(:[]).with(:quota).returns "" - @resource.stubs(:[]).with(:mountpoint).returns "/foo" - properties = @provider.add_properties - properties.include?("-o").should == true - properties.include?("mountpoint=/foo").should == true - properties.detect { |a| a.include?("quota") }.should == nil - end - end - - describe "when calling create" do - it "should call add_properties" do - @provider.stubs(:zfs) - @provider.expects(:add_properties).returns([]) - @provider.create - end - - it "should call zfs with create, properties and this zfs" do - @provider.stubs(:add_properties).returns(%w{a b}) - @provider.expects(:zfs).with(:create, "a", "b", @resource[:name]) - @provider.create - end - end - - describe "when calling destroy" do - it "should call zfs with :destroy and this zfs" do - @provider.expects(:zfs).with(:destroy, @resource[:name]) - @provider.destroy - end - end - - describe "when calling exist?" do - it "should call zfs with :list" do - #return stuff because we have to slice and dice it - @provider.expects(:zfs).with(:list).returns("NAME USED AVAIL REFER MOUNTPOINT\nmyzfs 100K 27.4M /myzfs") - @provider.exists? - end - - it "should return true if returned values match the name" do - @provider.stubs(:zfs).with(:list).returns("NAME USED AVAIL REFER MOUNTPOINT\n#{@resource[:name]} 100K 27.4M /myzfs") - @provider.exists?.should == true - end - - it "should return false if returned values don't match the name" do - @provider.stubs(:zfs).with(:list).returns("no soup for you") - @provider.exists?.should == false - end - - end - - [:mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |prop| - describe "when getting the #{prop} value" do - it "should call zfs with :get, #{prop} and this zfs" do - @provider.expects(:zfs).with(:get, "-H", "-o", "value", prop, @resource[:name]).returns("value\n") - @provider.send(prop) - end - - it "should get the third value of the second line from the output" do - @provider.stubs(:zfs).with(:get, "-H", "-o", "value", prop, @resource[:name]).returns("value\n") - @provider.send(prop).should == "value" - end - end - - describe "when setting the #{prop} value" do - it "should call zfs with :set, #{prop}=value and this zfs" do - @provider.expects(:zfs).with(:set, "#{prop}=value", @resource[:name]) - @provider.send("#{prop}=".intern, "value") - end - end - end - -end diff --git a/spec/unit/provider/zone/solaris_spec.rb b/spec/unit/provider/zone/solaris_spec.rb new file mode 100755 index 000000000..0459e5e6c --- /dev/null +++ b/spec/unit/provider/zone/solaris_spec.rb @@ -0,0 +1,55 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:zone).provider(:solaris) + +describe provider_class do + before do + @resource = stub("resource", :name => "mypool") + @resource.stubs(:[]).returns "shouldvalue" + @provider = provider_class.new(@resource) + end + + describe "when calling configure" do + it "should add the create args to the create str" do + @resource.stubs(:properties).returns([]) + @resource.stubs(:[]).with(:create_args).returns("create_args") + @provider.expects(:setconfig).with("create -b create_args\nset zonepath=shouldvalue\ncommit\n") + @provider.configure + end + end + + describe "when installing" do + it "should call zoneadm" do + @provider.expects(:zoneadm) + @provider.install + end + + describe "when cloning" do + before { @resource.stubs(:[]).with(:clone).returns(:clone_argument) } + + it "sohuld clone with the resource's clone attribute" do + @provider.expects(:zoneadm).with(:clone, :clone_argument) + @provider.install + end + end + + describe "when not cloning" do + before { @resource.stubs(:[]).with(:clone).returns(nil)} + + it "should just install if there are no install args" do + @resource.stubs(:[]).with(:install_args).returns(nil) + @provider.expects(:zoneadm).with(:install) + @provider.install + end + + it "should add the install args to the command if they exist" do + @resource.stubs(:[]).with(:install_args).returns("install args") + @provider.expects(:zoneadm).with(:install, ["install", "args"]) + @provider.install + end + end + end + +end diff --git a/spec/unit/provider/zone/solaris_spec_spec.rb b/spec/unit/provider/zone/solaris_spec_spec.rb deleted file mode 100755 index 0459e5e6c..000000000 --- a/spec/unit/provider/zone/solaris_spec_spec.rb +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:zone).provider(:solaris) - -describe provider_class do - before do - @resource = stub("resource", :name => "mypool") - @resource.stubs(:[]).returns "shouldvalue" - @provider = provider_class.new(@resource) - end - - describe "when calling configure" do - it "should add the create args to the create str" do - @resource.stubs(:properties).returns([]) - @resource.stubs(:[]).with(:create_args).returns("create_args") - @provider.expects(:setconfig).with("create -b create_args\nset zonepath=shouldvalue\ncommit\n") - @provider.configure - end - end - - describe "when installing" do - it "should call zoneadm" do - @provider.expects(:zoneadm) - @provider.install - end - - describe "when cloning" do - before { @resource.stubs(:[]).with(:clone).returns(:clone_argument) } - - it "sohuld clone with the resource's clone attribute" do - @provider.expects(:zoneadm).with(:clone, :clone_argument) - @provider.install - end - end - - describe "when not cloning" do - before { @resource.stubs(:[]).with(:clone).returns(nil)} - - it "should just install if there are no install args" do - @resource.stubs(:[]).with(:install_args).returns(nil) - @provider.expects(:zoneadm).with(:install) - @provider.install - end - - it "should add the install args to the command if they exist" do - @resource.stubs(:[]).with(:install_args).returns("install args") - @provider.expects(:zoneadm).with(:install, ["install", "args"]) - @provider.install - end - end - end - -end diff --git a/spec/unit/provider/zpool/solaris_spec.rb b/spec/unit/provider/zpool/solaris_spec.rb new file mode 100755 index 000000000..fc8336f32 --- /dev/null +++ b/spec/unit/provider/zpool/solaris_spec.rb @@ -0,0 +1,179 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:zpool).provider(:solaris) + +describe provider_class do + before do + @resource = stub("resource", :name => "mypool") + @resource.stubs(:[]).returns "shouldvalue" + @provider = provider_class.new(@resource) + end + + describe "when getting the instance" do + it "should call process_zpool_data with the result of get_pool_data only once" do + @provider.stubs(:get_pool_data).returns(["foo", "disk"]) + @provider.expects(:process_zpool_data).with(["foo", "disk"]).returns("stuff").once + @provider.current_pool + @provider.current_pool + end + end + + describe "when calling flush" do + it "should need to reload the pool" do + @provider.stubs(:get_pool_data) + @provider.expects(:process_zpool_data).returns("stuff").times(2) + @provider.current_pool + @provider.flush + @provider.current_pool + end + end + + describe "when procesing zpool data" do + before do + @zpool_data = ["foo", "disk"] + end + + describe "when there is no data" do + it "should return a hash with ensure=>:absent" do + @provider.process_zpool_data([])[:ensure].should == :absent + end + end + + describe "when there is a spare" do + it "should add the spare disk to the hash" do + @zpool_data += ["spares", "spare_disk"] + @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk"] + end + end + + describe "when there are two spares" do + it "should add the spare disk to the hash as a single string" do + @zpool_data += ["spares", "spare_disk", "spare_disk2"] + @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk spare_disk2"] + end + end + + describe "when there is a log" do + it "should add the log disk to the hash" do + @zpool_data += ["logs", "log_disk"] + @provider.process_zpool_data(@zpool_data)[:log].should == ["log_disk"] + end + end + + describe "when there are two logs" do + it "should add the log disks to the hash as a single string" do + @zpool_data += ["spares", "spare_disk", "spare_disk2"] + @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk spare_disk2"] + end + end + + describe "when the vdev is a single mirror" do + it "should call create_multi_array with mirror" do + @zpool_data = ["mirrorpool", "mirror", "disk1", "disk2"] + @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2"] + end + end + + describe "when the vdev is a double mirror" do + it "should call create_multi_array with mirror" do + @zpool_data = ["mirrorpool", "mirror", "disk1", "disk2", "mirror", "disk3", "disk4"] + @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2", "disk3 disk4"] + end + end + + describe "when the vdev is a raidz1" do + it "should call create_multi_array with raidz1" do + @zpool_data = ["mirrorpool", "raidz1", "disk1", "disk2"] + @provider.process_zpool_data(@zpool_data)[:raidz].should == ["disk1 disk2"] + end + end + + describe "when the vdev is a raidz2" do + it "should call create_multi_array with raidz2 and set the raid_parity" do + @zpool_data = ["mirrorpool", "raidz2", "disk1", "disk2"] + pool = @provider.process_zpool_data(@zpool_data) + pool[:raidz].should == ["disk1 disk2"] + pool[:raid_parity].should == "raidz2" + end + end + end + + describe "when calling the getters and setters" do + [:disk, :mirror, :raidz, :log, :spare].each do |field| + describe "when calling %s" % field do + it "should get the %s value from the current_pool hash" % field do + pool_hash = mock "pool hash" + pool_hash.expects(:[]).with(field) + @provider.stubs(:current_pool).returns(pool_hash) + @provider.send(field) + end + end + + describe "when setting the %s" % field do + it "should warn the %s values were not in sync" % field do + Puppet.expects(:warning).with("NO CHANGES BEING MADE: zpool %s does not match, should be 'shouldvalue' currently is 'currentvalue'" % field) + @provider.stubs(:current_pool).returns(Hash.new("currentvalue")) + @provider.send((field.to_s + "=").intern, "shouldvalue") + end + end + end + end + + describe "when calling create" do + before do + @resource.stubs(:[]).with(:pool).returns("mypool") + @provider.stubs(:zpool) + end + + + it "should call build_vdevs" do + @provider.expects(:build_vdevs).returns([]) + @provider.create + end + + it "should call build_named with 'spares' and 'log" do + @provider.expects(:build_named).with("spare").returns([]) + @provider.expects(:build_named).with("log").returns([]) + @provider.create + end + + it "should call zpool with arguments from build_vdevs and build_named" do + @provider.expects(:zpool).with(:create, 'mypool', 'shouldvalue', 'spare', 'shouldvalue', 'log', 'shouldvalue') + @provider.create + end + end + + describe "when calling delete" do + it "should call zpool with destroy and the pool name" do + @resource.stubs(:[]).with(:pool).returns("poolname") + @provider.expects(:zpool).with(:destroy, "poolname") + @provider.delete + end + end + + describe "when calling exists?" do + before do + @current_pool = Hash.new(:absent) + @provider.stubs(:get_pool_data).returns([]) + @provider.stubs(:process_zpool_data).returns(@current_pool) + end + + it "should get the current pool" do + @provider.expects(:process_zpool_data).returns(@current_pool) + @provider.exists? + end + + it "should return false if the current_pool is absent" do + #the before sets it up + @provider.exists?.should == false + end + + it "should return true if the current_pool has values" do + @current_pool[:pool] = "mypool" + @provider.exists?.should == true + end + end + +end diff --git a/spec/unit/provider/zpool/solaris_spec_spec.rb b/spec/unit/provider/zpool/solaris_spec_spec.rb deleted file mode 100755 index fc8336f32..000000000 --- a/spec/unit/provider/zpool/solaris_spec_spec.rb +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -provider_class = Puppet::Type.type(:zpool).provider(:solaris) - -describe provider_class do - before do - @resource = stub("resource", :name => "mypool") - @resource.stubs(:[]).returns "shouldvalue" - @provider = provider_class.new(@resource) - end - - describe "when getting the instance" do - it "should call process_zpool_data with the result of get_pool_data only once" do - @provider.stubs(:get_pool_data).returns(["foo", "disk"]) - @provider.expects(:process_zpool_data).with(["foo", "disk"]).returns("stuff").once - @provider.current_pool - @provider.current_pool - end - end - - describe "when calling flush" do - it "should need to reload the pool" do - @provider.stubs(:get_pool_data) - @provider.expects(:process_zpool_data).returns("stuff").times(2) - @provider.current_pool - @provider.flush - @provider.current_pool - end - end - - describe "when procesing zpool data" do - before do - @zpool_data = ["foo", "disk"] - end - - describe "when there is no data" do - it "should return a hash with ensure=>:absent" do - @provider.process_zpool_data([])[:ensure].should == :absent - end - end - - describe "when there is a spare" do - it "should add the spare disk to the hash" do - @zpool_data += ["spares", "spare_disk"] - @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk"] - end - end - - describe "when there are two spares" do - it "should add the spare disk to the hash as a single string" do - @zpool_data += ["spares", "spare_disk", "spare_disk2"] - @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk spare_disk2"] - end - end - - describe "when there is a log" do - it "should add the log disk to the hash" do - @zpool_data += ["logs", "log_disk"] - @provider.process_zpool_data(@zpool_data)[:log].should == ["log_disk"] - end - end - - describe "when there are two logs" do - it "should add the log disks to the hash as a single string" do - @zpool_data += ["spares", "spare_disk", "spare_disk2"] - @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk spare_disk2"] - end - end - - describe "when the vdev is a single mirror" do - it "should call create_multi_array with mirror" do - @zpool_data = ["mirrorpool", "mirror", "disk1", "disk2"] - @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2"] - end - end - - describe "when the vdev is a double mirror" do - it "should call create_multi_array with mirror" do - @zpool_data = ["mirrorpool", "mirror", "disk1", "disk2", "mirror", "disk3", "disk4"] - @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2", "disk3 disk4"] - end - end - - describe "when the vdev is a raidz1" do - it "should call create_multi_array with raidz1" do - @zpool_data = ["mirrorpool", "raidz1", "disk1", "disk2"] - @provider.process_zpool_data(@zpool_data)[:raidz].should == ["disk1 disk2"] - end - end - - describe "when the vdev is a raidz2" do - it "should call create_multi_array with raidz2 and set the raid_parity" do - @zpool_data = ["mirrorpool", "raidz2", "disk1", "disk2"] - pool = @provider.process_zpool_data(@zpool_data) - pool[:raidz].should == ["disk1 disk2"] - pool[:raid_parity].should == "raidz2" - end - end - end - - describe "when calling the getters and setters" do - [:disk, :mirror, :raidz, :log, :spare].each do |field| - describe "when calling %s" % field do - it "should get the %s value from the current_pool hash" % field do - pool_hash = mock "pool hash" - pool_hash.expects(:[]).with(field) - @provider.stubs(:current_pool).returns(pool_hash) - @provider.send(field) - end - end - - describe "when setting the %s" % field do - it "should warn the %s values were not in sync" % field do - Puppet.expects(:warning).with("NO CHANGES BEING MADE: zpool %s does not match, should be 'shouldvalue' currently is 'currentvalue'" % field) - @provider.stubs(:current_pool).returns(Hash.new("currentvalue")) - @provider.send((field.to_s + "=").intern, "shouldvalue") - end - end - end - end - - describe "when calling create" do - before do - @resource.stubs(:[]).with(:pool).returns("mypool") - @provider.stubs(:zpool) - end - - - it "should call build_vdevs" do - @provider.expects(:build_vdevs).returns([]) - @provider.create - end - - it "should call build_named with 'spares' and 'log" do - @provider.expects(:build_named).with("spare").returns([]) - @provider.expects(:build_named).with("log").returns([]) - @provider.create - end - - it "should call zpool with arguments from build_vdevs and build_named" do - @provider.expects(:zpool).with(:create, 'mypool', 'shouldvalue', 'spare', 'shouldvalue', 'log', 'shouldvalue') - @provider.create - end - end - - describe "when calling delete" do - it "should call zpool with destroy and the pool name" do - @resource.stubs(:[]).with(:pool).returns("poolname") - @provider.expects(:zpool).with(:destroy, "poolname") - @provider.delete - end - end - - describe "when calling exists?" do - before do - @current_pool = Hash.new(:absent) - @provider.stubs(:get_pool_data).returns([]) - @provider.stubs(:process_zpool_data).returns(@current_pool) - end - - it "should get the current pool" do - @provider.expects(:process_zpool_data).returns(@current_pool) - @provider.exists? - end - - it "should return false if the current_pool is absent" do - #the before sets it up - @provider.exists?.should == false - end - - it "should return true if the current_pool has values" do - @current_pool[:pool] = "mypool" - @provider.exists?.should == true - end - end - -end diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb new file mode 100755 index 000000000..9d781540b --- /dev/null +++ b/spec/unit/provider_spec.rb @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +describe Puppet::Provider do + it "should have a specifity class method" do + Puppet::Provider.should respond_to(:specificity) + end + + it "should consider two defaults to be higher specificity than one default" do + one = Class.new(Puppet::Provider) + one.initvars + one.defaultfor :operatingsystem => "solaris" + + two = Class.new(Puppet::Provider) + two.initvars + two.defaultfor :operatingsystem => "solaris", :operatingsystemrelease => "5.10" + + two.specificity.should > one.specificity + end + + it "should consider a subclass more specific than its parent class" do + one = Class.new(Puppet::Provider) + one.initvars + + two = Class.new(one) + two.initvars + + two.specificity.should > one.specificity + end +end diff --git a/spec/unit/provider_spec_spec.rb b/spec/unit/provider_spec_spec.rb deleted file mode 100755 index 9d781540b..000000000 --- a/spec/unit/provider_spec_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -describe Puppet::Provider do - it "should have a specifity class method" do - Puppet::Provider.should respond_to(:specificity) - end - - it "should consider two defaults to be higher specificity than one default" do - one = Class.new(Puppet::Provider) - one.initvars - one.defaultfor :operatingsystem => "solaris" - - two = Class.new(Puppet::Provider) - two.initvars - two.defaultfor :operatingsystem => "solaris", :operatingsystemrelease => "5.10" - - two.specificity.should > one.specificity - end - - it "should consider a subclass more specific than its parent class" do - one = Class.new(Puppet::Provider) - one.initvars - - two = Class.new(one) - two.initvars - - two.specificity.should > one.specificity - end -end diff --git a/spec/unit/puppet_spec.rb b/spec/unit/puppet_spec.rb new file mode 100755 index 000000000..07834299a --- /dev/null +++ b/spec/unit/puppet_spec.rb @@ -0,0 +1,12 @@ +#!/usr/bin/env ruby" + +require File.dirname(__FILE__) + '/../spec_helper' +require 'puppet' + +describe Puppet do + Puppet::Util::Log.eachlevel do |level| + it "should have a method for sending '#{level}' logs" do + Puppet.should respond_to(level) + end + end +end diff --git a/spec/unit/puppet_spec_spec.rb b/spec/unit/puppet_spec_spec.rb deleted file mode 100755 index 07834299a..000000000 --- a/spec/unit/puppet_spec_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env ruby" - -require File.dirname(__FILE__) + '/../spec_helper' -require 'puppet' - -describe Puppet do - Puppet::Util::Log.eachlevel do |level| - it "should have a method for sending '#{level}' logs" do - Puppet.should respond_to(level) - end - end -end diff --git a/spec/unit/rails/host_spec.rb b/spec/unit/rails/host_spec.rb new file mode 100755 index 000000000..ac0896350 --- /dev/null +++ b/spec/unit/rails/host_spec.rb @@ -0,0 +1,163 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Puppet::Rails::Host" do + confine "Cannot test without ActiveRecord" => Puppet.features.rails? + + def column(name, type) + ActiveRecord::ConnectionAdapters::Column.new(name, nil, type, false) + end + + before do + require 'puppet/rails/host' + + # Stub this so we don't need access to the DB. + Puppet::Rails::Host.stubs(:columns).returns([column("name", "string"), column("environment", "string"), column("ip", "string")]) + + @node = Puppet::Node.new("foo") + @node.environment = "production" + @node.ipaddress = "127.0.0.1" + + @host = stub 'host', :environment= => nil, :ip= => nil + end + + describe "when converting a Puppet::Node instance into a Rails instance" do + it "should modify any existing instance in the database" do + Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host + + Puppet::Rails::Host.from_puppet(@node) + end + + it "should create a new instance in the database if none can be found" do + Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil + Puppet::Rails::Host.expects(:new).with(:name => "foo").returns @host + + Puppet::Rails::Host.from_puppet(@node) + end + + it "should copy the environment from the Puppet instance" do + Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host + + @node.environment = "production" + @host.expects(:environment=).with {|x| x.name.to_s == 'production' } + + Puppet::Rails::Host.from_puppet(@node) + end + + it "should copy the ipaddress from the Puppet instance" do + Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host + + @node.ipaddress = "192.168.0.1" + @host.expects(:ip=).with "192.168.0.1" + + Puppet::Rails::Host.from_puppet(@node) + end + + it "should not save the Rails instance" do + Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host + + @host.expects(:save).never + + Puppet::Rails::Host.from_puppet(@node) + end + end + + describe "when converting a Puppet::Rails::Host instance into a Puppet::Node instance" do + before do + @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") + @node = Puppet::Node.new("foo") + Puppet::Node.stubs(:new).with("foo").returns @node + end + + it "should create a new instance with the correct name" do + Puppet::Node.expects(:new).with("foo").returns @node + + @host.to_puppet + end + + it "should copy the environment from the Rails instance" do + @host.environment = "prod" + @node.expects(:environment=).with "prod" + @host.to_puppet + end + + it "should copy the ipaddress from the Rails instance" do + @host.ip = "192.168.0.1" + @node.expects(:ipaddress=).with "192.168.0.1" + @host.to_puppet + end + end + + describe "when merging catalog resources and database resources" do + before :each do + Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(false) + @resource1 = stub_everything 'res1' + @resource2 = stub_everything 'res2' + @resources = [ @resource1, @resource2 ] + + @dbresource1 = stub_everything 'dbres1' + @dbresource2 = stub_everything 'dbres2' + @dbresources = { 1 => @dbresource1, 2 => @dbresource2 } + + @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") + @host.stubs(:find_resources).returns(@dbresources) + @host.stubs(:find_resources_parameters_tags) + @host.stubs(:compare_to_catalog) + @host.stubs(:id).returns(1) + end + + it "should find all database resources" do + @host.expects(:find_resources) + + @host.merge_resources(@resources) + end + + it "should find all paramaters and tags for those database resources" do + @host.expects(:find_resources_parameters_tags).with(@dbresources) + + @host.merge_resources(@resources) + end + + it "should compare all database resources to catalog" do + @host.expects(:compare_to_catalog).with(@dbresources, @resources) + + @host.merge_resources(@resources) + end + + it "should compare only exported resources in thin_storeconfigs mode" do + Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(true) + @resource1.stubs(:exported?).returns(true) + + @host.expects(:compare_to_catalog).with(@dbresources, [ @resource1 ]) + + @host.merge_resources(@resources) + end + end + + describe "when searching the database for host resources" do + before :each do + Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(false) + @resource1 = stub_everything 'res1', :id => 1 + @resource2 = stub_everything 'res2', :id => 2 + @resources = [ @resource1, @resource2 ] + + @dbresources = stub 'resources' + @dbresources.stubs(:find).returns(@resources) + + @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") + @host.stubs(:resources).returns(@dbresources) + end + + it "should return a hash keyed by id of all resources" do + @host.find_resources.should == { 1 => @resource1, 2 => @resource2 } + end + + it "should return a hash keyed by id of only exported resources in thin_storeconfigs mode" do + Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(true) + @dbresources.expects(:find).with { |*h| h[1][:conditions] == { :exported => true } }.returns([]) + + @host.find_resources + end + end +end diff --git a/spec/unit/rails/host_spec_spec.rb b/spec/unit/rails/host_spec_spec.rb deleted file mode 100755 index ac0896350..000000000 --- a/spec/unit/rails/host_spec_spec.rb +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe "Puppet::Rails::Host" do - confine "Cannot test without ActiveRecord" => Puppet.features.rails? - - def column(name, type) - ActiveRecord::ConnectionAdapters::Column.new(name, nil, type, false) - end - - before do - require 'puppet/rails/host' - - # Stub this so we don't need access to the DB. - Puppet::Rails::Host.stubs(:columns).returns([column("name", "string"), column("environment", "string"), column("ip", "string")]) - - @node = Puppet::Node.new("foo") - @node.environment = "production" - @node.ipaddress = "127.0.0.1" - - @host = stub 'host', :environment= => nil, :ip= => nil - end - - describe "when converting a Puppet::Node instance into a Rails instance" do - it "should modify any existing instance in the database" do - Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host - - Puppet::Rails::Host.from_puppet(@node) - end - - it "should create a new instance in the database if none can be found" do - Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil - Puppet::Rails::Host.expects(:new).with(:name => "foo").returns @host - - Puppet::Rails::Host.from_puppet(@node) - end - - it "should copy the environment from the Puppet instance" do - Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host - - @node.environment = "production" - @host.expects(:environment=).with {|x| x.name.to_s == 'production' } - - Puppet::Rails::Host.from_puppet(@node) - end - - it "should copy the ipaddress from the Puppet instance" do - Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host - - @node.ipaddress = "192.168.0.1" - @host.expects(:ip=).with "192.168.0.1" - - Puppet::Rails::Host.from_puppet(@node) - end - - it "should not save the Rails instance" do - Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host - - @host.expects(:save).never - - Puppet::Rails::Host.from_puppet(@node) - end - end - - describe "when converting a Puppet::Rails::Host instance into a Puppet::Node instance" do - before do - @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") - @node = Puppet::Node.new("foo") - Puppet::Node.stubs(:new).with("foo").returns @node - end - - it "should create a new instance with the correct name" do - Puppet::Node.expects(:new).with("foo").returns @node - - @host.to_puppet - end - - it "should copy the environment from the Rails instance" do - @host.environment = "prod" - @node.expects(:environment=).with "prod" - @host.to_puppet - end - - it "should copy the ipaddress from the Rails instance" do - @host.ip = "192.168.0.1" - @node.expects(:ipaddress=).with "192.168.0.1" - @host.to_puppet - end - end - - describe "when merging catalog resources and database resources" do - before :each do - Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(false) - @resource1 = stub_everything 'res1' - @resource2 = stub_everything 'res2' - @resources = [ @resource1, @resource2 ] - - @dbresource1 = stub_everything 'dbres1' - @dbresource2 = stub_everything 'dbres2' - @dbresources = { 1 => @dbresource1, 2 => @dbresource2 } - - @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") - @host.stubs(:find_resources).returns(@dbresources) - @host.stubs(:find_resources_parameters_tags) - @host.stubs(:compare_to_catalog) - @host.stubs(:id).returns(1) - end - - it "should find all database resources" do - @host.expects(:find_resources) - - @host.merge_resources(@resources) - end - - it "should find all paramaters and tags for those database resources" do - @host.expects(:find_resources_parameters_tags).with(@dbresources) - - @host.merge_resources(@resources) - end - - it "should compare all database resources to catalog" do - @host.expects(:compare_to_catalog).with(@dbresources, @resources) - - @host.merge_resources(@resources) - end - - it "should compare only exported resources in thin_storeconfigs mode" do - Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(true) - @resource1.stubs(:exported?).returns(true) - - @host.expects(:compare_to_catalog).with(@dbresources, [ @resource1 ]) - - @host.merge_resources(@resources) - end - end - - describe "when searching the database for host resources" do - before :each do - Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(false) - @resource1 = stub_everything 'res1', :id => 1 - @resource2 = stub_everything 'res2', :id => 2 - @resources = [ @resource1, @resource2 ] - - @dbresources = stub 'resources' - @dbresources.stubs(:find).returns(@resources) - - @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") - @host.stubs(:resources).returns(@dbresources) - end - - it "should return a hash keyed by id of all resources" do - @host.find_resources.should == { 1 => @resource1, 2 => @resource2 } - end - - it "should return a hash keyed by id of only exported resources in thin_storeconfigs mode" do - Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(true) - @dbresources.expects(:find).with { |*h| h[1][:conditions] == { :exported => true } }.returns([]) - - @host.find_resources - end - end -end diff --git a/spec/unit/rails/param_value_spec.rb b/spec/unit/rails/param_value_spec.rb new file mode 100755 index 000000000..070ac82ea --- /dev/null +++ b/spec/unit/rails/param_value_spec.rb @@ -0,0 +1,49 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Puppet::Rails::ParamValue" do + confine "Cannot test without ActiveRecord" => Puppet.features.rails? + + def column(name, type) + ActiveRecord::ConnectionAdapters::Column.new(name, nil, type, false) + end + + before do + require 'puppet/rails/param_value' + + name = stub 'param_name', :name => "foo" + + # Stub this so we don't need access to the DB. + Puppet::Rails::ParamValue.stubs(:columns).returns([column("value", "string")]) + Puppet::Rails::ParamName.stubs(:find_or_create_by_name).returns(name) + end + + describe "when creating initial parameter values" do + it "should return an array of hashes" do + Puppet::Rails::ParamValue.from_parser_param(:myparam, %w{a b})[0].should be_instance_of(Hash) + end + + it "should return hashes for each value with the parameter name set as the ParamName instance" do + name = stub 'param_name', :name => "foo" + Puppet::Rails::ParamName.expects(:find_or_create_by_name).returns(name) + + result = Puppet::Rails::ParamValue.from_parser_param(:myparam, "a")[0] + result[:value].should == "a" + result[:param_name].should == name + end + + it "should return an array of hashes even when only one parameter is provided" do + Puppet::Rails::ParamValue.from_parser_param(:myparam, "a")[0].should be_instance_of(Hash) + end + + it "should convert all arguments into strings" do + Puppet::Rails::ParamValue.from_parser_param(:myparam, 50)[0][:value].should == "50" + end + + it "should not convert Resource References into strings" do + ref = Puppet::Resource.new(:file, "/file") + Puppet::Rails::ParamValue.from_parser_param(:myparam, ref)[0][:value].should == ref + end + end +end diff --git a/spec/unit/rails/param_value_spec_spec.rb b/spec/unit/rails/param_value_spec_spec.rb deleted file mode 100755 index 070ac82ea..000000000 --- a/spec/unit/rails/param_value_spec_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe "Puppet::Rails::ParamValue" do - confine "Cannot test without ActiveRecord" => Puppet.features.rails? - - def column(name, type) - ActiveRecord::ConnectionAdapters::Column.new(name, nil, type, false) - end - - before do - require 'puppet/rails/param_value' - - name = stub 'param_name', :name => "foo" - - # Stub this so we don't need access to the DB. - Puppet::Rails::ParamValue.stubs(:columns).returns([column("value", "string")]) - Puppet::Rails::ParamName.stubs(:find_or_create_by_name).returns(name) - end - - describe "when creating initial parameter values" do - it "should return an array of hashes" do - Puppet::Rails::ParamValue.from_parser_param(:myparam, %w{a b})[0].should be_instance_of(Hash) - end - - it "should return hashes for each value with the parameter name set as the ParamName instance" do - name = stub 'param_name', :name => "foo" - Puppet::Rails::ParamName.expects(:find_or_create_by_name).returns(name) - - result = Puppet::Rails::ParamValue.from_parser_param(:myparam, "a")[0] - result[:value].should == "a" - result[:param_name].should == name - end - - it "should return an array of hashes even when only one parameter is provided" do - Puppet::Rails::ParamValue.from_parser_param(:myparam, "a")[0].should be_instance_of(Hash) - end - - it "should convert all arguments into strings" do - Puppet::Rails::ParamValue.from_parser_param(:myparam, 50)[0][:value].should == "50" - end - - it "should not convert Resource References into strings" do - ref = Puppet::Resource.new(:file, "/file") - Puppet::Rails::ParamValue.from_parser_param(:myparam, ref)[0][:value].should == ref - end - end -end diff --git a/spec/unit/rails/resource_spec.rb b/spec/unit/rails/resource_spec.rb new file mode 100755 index 000000000..e9162b92e --- /dev/null +++ b/spec/unit/rails/resource_spec.rb @@ -0,0 +1,107 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Puppet::Rails::Resource" do + confine "Cannot test without ActiveRecord" => Puppet.features.rails? + + def column(name, type) + ActiveRecord::ConnectionAdapters::Column.new(name, nil, type, false) + end + + before do + require 'puppet/rails/resource' + + # Stub this so we don't need access to the DB. + Puppet::Rails::Resource.stubs(:columns).returns([column("title", "string"), column("restype", "string"), column("exported", "boolean")]) + end + + describe "when creating initial resource arguments" do + it "should set the restype to the resource's type" do + Puppet::Rails::Resource.rails_resource_initial_args(Puppet::Resource.new(:file, "/file"))[:restype].should == "File" + end + + it "should set the title to the resource's title" do + Puppet::Rails::Resource.rails_resource_initial_args(Puppet::Resource.new(:file, "/file"))[:title].should == "/file" + end + + it "should set the line to the resource's line if one is available" do + resource = Puppet::Resource.new(:file, "/file") + resource.line = 50 + + Puppet::Rails::Resource.rails_resource_initial_args(resource)[:line].should == 50 + end + + it "should set 'exported' to true of the resource is exported" do + resource = Puppet::Resource.new(:file, "/file") + resource.exported = true + + Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_true + end + + it "should set 'exported' to false of the resource is not exported" do + resource = Puppet::Resource.new(:file, "/file") + resource.exported = false + + Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_false + + resource = Puppet::Resource.new(:file, "/file") + resource.exported = nil + + Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_false + end + end + + describe "when merging in a parser resource" do + before do + @parser = mock 'parser resource' + + @resource = Puppet::Rails::Resource.new + [:merge_attributes, :merge_parameters, :merge_tags, :save].each { |m| @resource.stubs(m) } + end + + it "should merge the attributes" do + @resource.expects(:merge_attributes).with(@parser) + + @resource.merge_parser_resource(@parser) + end + + it "should merge the parameters" do + @resource.expects(:merge_parameters).with(@parser) + + @resource.merge_parser_resource(@parser) + end + + it "should merge the tags" do + @resource.expects(:merge_tags).with(@parser) + + @resource.merge_parser_resource(@parser) + end + + it "should save itself" do + @resource.expects(:save) + + @resource.merge_parser_resource(@parser) + end + end + + describe "merge_parameters" do + it "should replace values that have changed" do + @resource = Puppet::Rails::Resource.new + @resource.params_list = [{"name" => "replace", "value" => 1, "id" => 100 }] + + Puppet::Rails::ParamValue.expects(:delete).with([100]) + param_values = stub "param_values" + param_values.expects(:build).with({:value=>nil, :param_name=>nil, :line=>{"replace"=>2}}) + @resource.stubs(:param_values).returns(param_values) + + Puppet::Rails::ParamName.stubs(:accumulate_by_name) + + merge_resource = stub "merge_resource" + merge_resource.expects(:line).returns({ "replace" => 2 }) + merge_resource.stubs(:each).yields([["replace", 2]]) + + @resource.merge_parameters(merge_resource) + end + end +end diff --git a/spec/unit/rails/resource_spec_spec.rb b/spec/unit/rails/resource_spec_spec.rb deleted file mode 100755 index e9162b92e..000000000 --- a/spec/unit/rails/resource_spec_spec.rb +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe "Puppet::Rails::Resource" do - confine "Cannot test without ActiveRecord" => Puppet.features.rails? - - def column(name, type) - ActiveRecord::ConnectionAdapters::Column.new(name, nil, type, false) - end - - before do - require 'puppet/rails/resource' - - # Stub this so we don't need access to the DB. - Puppet::Rails::Resource.stubs(:columns).returns([column("title", "string"), column("restype", "string"), column("exported", "boolean")]) - end - - describe "when creating initial resource arguments" do - it "should set the restype to the resource's type" do - Puppet::Rails::Resource.rails_resource_initial_args(Puppet::Resource.new(:file, "/file"))[:restype].should == "File" - end - - it "should set the title to the resource's title" do - Puppet::Rails::Resource.rails_resource_initial_args(Puppet::Resource.new(:file, "/file"))[:title].should == "/file" - end - - it "should set the line to the resource's line if one is available" do - resource = Puppet::Resource.new(:file, "/file") - resource.line = 50 - - Puppet::Rails::Resource.rails_resource_initial_args(resource)[:line].should == 50 - end - - it "should set 'exported' to true of the resource is exported" do - resource = Puppet::Resource.new(:file, "/file") - resource.exported = true - - Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_true - end - - it "should set 'exported' to false of the resource is not exported" do - resource = Puppet::Resource.new(:file, "/file") - resource.exported = false - - Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_false - - resource = Puppet::Resource.new(:file, "/file") - resource.exported = nil - - Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_false - end - end - - describe "when merging in a parser resource" do - before do - @parser = mock 'parser resource' - - @resource = Puppet::Rails::Resource.new - [:merge_attributes, :merge_parameters, :merge_tags, :save].each { |m| @resource.stubs(m) } - end - - it "should merge the attributes" do - @resource.expects(:merge_attributes).with(@parser) - - @resource.merge_parser_resource(@parser) - end - - it "should merge the parameters" do - @resource.expects(:merge_parameters).with(@parser) - - @resource.merge_parser_resource(@parser) - end - - it "should merge the tags" do - @resource.expects(:merge_tags).with(@parser) - - @resource.merge_parser_resource(@parser) - end - - it "should save itself" do - @resource.expects(:save) - - @resource.merge_parser_resource(@parser) - end - end - - describe "merge_parameters" do - it "should replace values that have changed" do - @resource = Puppet::Rails::Resource.new - @resource.params_list = [{"name" => "replace", "value" => 1, "id" => 100 }] - - Puppet::Rails::ParamValue.expects(:delete).with([100]) - param_values = stub "param_values" - param_values.expects(:build).with({:value=>nil, :param_name=>nil, :line=>{"replace"=>2}}) - @resource.stubs(:param_values).returns(param_values) - - Puppet::Rails::ParamName.stubs(:accumulate_by_name) - - merge_resource = stub "merge_resource" - merge_resource.expects(:line).returns({ "replace" => 2 }) - merge_resource.stubs(:each).yields([["replace", 2]]) - - @resource.merge_parameters(merge_resource) - end - end -end diff --git a/spec/unit/rails_spec.rb b/spec/unit/rails_spec.rb new file mode 100755 index 000000000..dd8d7def4 --- /dev/null +++ b/spec/unit/rails_spec.rb @@ -0,0 +1,258 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' +require 'puppet/rails' + +describe Puppet::Rails, "when initializing any connection" do + confine "Cannot test without ActiveRecord" => Puppet.features.rails? + + before do + Puppet.settings.stubs(:use) + @logger = mock 'logger' + @logger.stub_everything + Logger.stubs(:new).returns(@logger) + + ActiveRecord::Base.stubs(:logger).returns(@logger) + ActiveRecord::Base.stubs(:connected?).returns(false) + end + + it "should use settings" do + Puppet.settings.expects(:use).with(:main, :rails, :master) + + Puppet::Rails.connect + end + + it "should set up a logger with the appropriate Rails log file" do + logger = mock 'logger' + Logger.expects(:new).with(Puppet[:railslog]).returns(logger) + ActiveRecord::Base.expects(:logger=).with(logger) + + Puppet::Rails.connect + end + + it "should set the log level to whatever the value is in the settings" do + Puppet.settings.stubs(:use) + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("debug") + Puppet.settings.stubs(:value).with(:railslog).returns("/my/file") + logger = mock 'logger' + Logger.stubs(:new).returns(logger) + ActiveRecord::Base.stubs(:logger).returns(logger) + logger.expects(:level=).with(Logger::DEBUG) + + ActiveRecord::Base.stubs(:allow_concurrency=) + ActiveRecord::Base.stubs(:verify_active_connections!) + ActiveRecord::Base.stubs(:establish_connection) + Puppet::Rails.stubs(:database_arguments).returns({}) + + Puppet::Rails.connect + end + + describe "on ActiveRecord 2.1.x" do + confine("ActiveRecord 2.1.x") { ::ActiveRecord::VERSION::MAJOR == 2 and ::ActiveRecord::VERSION::MINOR <= 1 } + + it "should set ActiveRecord::Base.allow_concurrency" do + ActiveRecord::Base.expects(:allow_concurrency=).with(true) + + Puppet::Rails.connect + end + end + + it "should call ActiveRecord::Base.verify_active_connections!" do + ActiveRecord::Base.expects(:verify_active_connections!) + + Puppet::Rails.connect + end + + it "should call ActiveRecord::Base.establish_connection with database_arguments" do + Puppet::Rails.expects(:database_arguments).returns({}) + ActiveRecord::Base.expects(:establish_connection) + + Puppet::Rails.connect + end +end + +describe Puppet::Rails, "when initializing a sqlite3 connection" do + confine "Cannot test without ActiveRecord" => Puppet.features.rails? + + it "should provide the adapter, log_level, and database arguments" do + Puppet.settings.expects(:value).with(:dbadapter).returns("sqlite3") + Puppet.settings.expects(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.expects(:value).with(:dblocation).returns("testlocation") + + Puppet::Rails.database_arguments.should == { + :adapter => "sqlite3", + :log_level => "testlevel", + :database => "testlocation" + } + end +end + +describe Puppet::Rails, "when initializing a mysql connection" do + confine "Cannot test without ActiveRecord" => Puppet.features.rails? + + it "should provide the adapter, log_level, and host, port, username, password, database, and reconnect arguments" do + Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") + Puppet.settings.stubs(:value).with(:dbport).returns("") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbsocket).returns("") + Puppet.settings.stubs(:value).with(:dbconnections).returns(1) + + Puppet::Rails.database_arguments.should == { + :adapter => "mysql", + :log_level => "testlevel", + :host => "testserver", + :username => "testuser", + :password => "testpassword", + :database => "testname", + :reconnect => true, + :pool => 1 + } + end + + it "should provide the adapter, log_level, and host, port, username, password, database, socket, connections, and reconnect arguments" do + Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") + Puppet.settings.stubs(:value).with(:dbport).returns("9999") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") + Puppet.settings.stubs(:value).with(:dbconnections).returns(1) + + Puppet::Rails.database_arguments.should == { + :adapter => "mysql", + :log_level => "testlevel", + :host => "testserver", + :port => "9999", + :username => "testuser", + :password => "testpassword", + :database => "testname", + :socket => "testsocket", + :reconnect => true, + :pool => 1 + } + end + + it "should provide the adapter, log_level, and host, port, username, password, database, socket, and connections arguments" do + Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") + Puppet.settings.stubs(:value).with(:dbport).returns("9999") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") + Puppet.settings.stubs(:value).with(:dbconnections).returns(1) + + Puppet::Rails.database_arguments.should == { + :adapter => "mysql", + :log_level => "testlevel", + :host => "testserver", + :port => "9999", + :username => "testuser", + :password => "testpassword", + :database => "testname", + :socket => "testsocket", + :reconnect => true, + :pool => 1 + } + end +end + +describe Puppet::Rails, "when initializing a postgresql connection" do + confine "Cannot test without ActiveRecord" => Puppet.features.rails? + + it "should provide the adapter, log_level, and host, port, username, password, connections, and database arguments" do + Puppet.settings.stubs(:value).with(:dbadapter).returns("postgresql") + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") + Puppet.settings.stubs(:value).with(:dbport).returns("9999") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbsocket).returns("") + Puppet.settings.stubs(:value).with(:dbconnections).returns(1) + + Puppet::Rails.database_arguments.should == { + :adapter => "postgresql", + :log_level => "testlevel", + :host => "testserver", + :port => "9999", + :username => "testuser", + :password => "testpassword", + :database => "testname", + :reconnect => true, + :pool => 1 + } + end + + it "should provide the adapter, log_level, and host, port, username, password, database, connections, and socket arguments" do + Puppet.settings.stubs(:value).with(:dbadapter).returns("postgresql") + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") + Puppet.settings.stubs(:value).with(:dbport).returns("9999") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") + Puppet.settings.stubs(:value).with(:dbconnections).returns(1) + + Puppet::Rails.database_arguments.should == { + :adapter => "postgresql", + :log_level => "testlevel", + :host => "testserver", + :port => "9999", + :username => "testuser", + :password => "testpassword", + :database => "testname", + :socket => "testsocket", + :pool => 1, + :reconnect => true + } + end +end + +describe Puppet::Rails, "when initializing an Oracle connection" do + confine "Cannot test without ActiveRecord" => Puppet.features.rails? + + it "should provide the adapter, log_level, and username, password, dbconnections, and database arguments" do + Puppet.settings.stubs(:value).with(:dbadapter).returns("oracle_enhanced") + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbconnections).returns(1) + + Puppet::Rails.database_arguments.should == { + :adapter => "oracle_enhanced", + :log_level => "testlevel", + :username => "testuser", + :password => "testpassword", + :database => "testname", + :pool => 1 + } + end + + it "should provide the adapter, log_level, and host, username, password, database, pool, and socket arguments" do + Puppet.settings.stubs(:value).with(:dbadapter).returns("oracle_enhanced") + Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") + Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") + Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") + Puppet.settings.stubs(:value).with(:dbname).returns("testname") + Puppet.settings.stubs(:value).with(:dbconnections).returns(1) + + Puppet::Rails.database_arguments.should == { + :adapter => "oracle_enhanced", + :log_level => "testlevel", + :username => "testuser", + :password => "testpassword", + :database => "testname", + :pool => 1 + } + end +end diff --git a/spec/unit/rails_spec_spec.rb b/spec/unit/rails_spec_spec.rb deleted file mode 100755 index dd8d7def4..000000000 --- a/spec/unit/rails_spec_spec.rb +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' -require 'puppet/rails' - -describe Puppet::Rails, "when initializing any connection" do - confine "Cannot test without ActiveRecord" => Puppet.features.rails? - - before do - Puppet.settings.stubs(:use) - @logger = mock 'logger' - @logger.stub_everything - Logger.stubs(:new).returns(@logger) - - ActiveRecord::Base.stubs(:logger).returns(@logger) - ActiveRecord::Base.stubs(:connected?).returns(false) - end - - it "should use settings" do - Puppet.settings.expects(:use).with(:main, :rails, :master) - - Puppet::Rails.connect - end - - it "should set up a logger with the appropriate Rails log file" do - logger = mock 'logger' - Logger.expects(:new).with(Puppet[:railslog]).returns(logger) - ActiveRecord::Base.expects(:logger=).with(logger) - - Puppet::Rails.connect - end - - it "should set the log level to whatever the value is in the settings" do - Puppet.settings.stubs(:use) - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("debug") - Puppet.settings.stubs(:value).with(:railslog).returns("/my/file") - logger = mock 'logger' - Logger.stubs(:new).returns(logger) - ActiveRecord::Base.stubs(:logger).returns(logger) - logger.expects(:level=).with(Logger::DEBUG) - - ActiveRecord::Base.stubs(:allow_concurrency=) - ActiveRecord::Base.stubs(:verify_active_connections!) - ActiveRecord::Base.stubs(:establish_connection) - Puppet::Rails.stubs(:database_arguments).returns({}) - - Puppet::Rails.connect - end - - describe "on ActiveRecord 2.1.x" do - confine("ActiveRecord 2.1.x") { ::ActiveRecord::VERSION::MAJOR == 2 and ::ActiveRecord::VERSION::MINOR <= 1 } - - it "should set ActiveRecord::Base.allow_concurrency" do - ActiveRecord::Base.expects(:allow_concurrency=).with(true) - - Puppet::Rails.connect - end - end - - it "should call ActiveRecord::Base.verify_active_connections!" do - ActiveRecord::Base.expects(:verify_active_connections!) - - Puppet::Rails.connect - end - - it "should call ActiveRecord::Base.establish_connection with database_arguments" do - Puppet::Rails.expects(:database_arguments).returns({}) - ActiveRecord::Base.expects(:establish_connection) - - Puppet::Rails.connect - end -end - -describe Puppet::Rails, "when initializing a sqlite3 connection" do - confine "Cannot test without ActiveRecord" => Puppet.features.rails? - - it "should provide the adapter, log_level, and database arguments" do - Puppet.settings.expects(:value).with(:dbadapter).returns("sqlite3") - Puppet.settings.expects(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.expects(:value).with(:dblocation).returns("testlocation") - - Puppet::Rails.database_arguments.should == { - :adapter => "sqlite3", - :log_level => "testlevel", - :database => "testlocation" - } - end -end - -describe Puppet::Rails, "when initializing a mysql connection" do - confine "Cannot test without ActiveRecord" => Puppet.features.rails? - - it "should provide the adapter, log_level, and host, port, username, password, database, and reconnect arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("") - Puppet.settings.stubs(:value).with(:dbconnections).returns(1) - - Puppet::Rails.database_arguments.should == { - :adapter => "mysql", - :log_level => "testlevel", - :host => "testserver", - :username => "testuser", - :password => "testpassword", - :database => "testname", - :reconnect => true, - :pool => 1 - } - end - - it "should provide the adapter, log_level, and host, port, username, password, database, socket, connections, and reconnect arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("9999") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") - Puppet.settings.stubs(:value).with(:dbconnections).returns(1) - - Puppet::Rails.database_arguments.should == { - :adapter => "mysql", - :log_level => "testlevel", - :host => "testserver", - :port => "9999", - :username => "testuser", - :password => "testpassword", - :database => "testname", - :socket => "testsocket", - :reconnect => true, - :pool => 1 - } - end - - it "should provide the adapter, log_level, and host, port, username, password, database, socket, and connections arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("9999") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") - Puppet.settings.stubs(:value).with(:dbconnections).returns(1) - - Puppet::Rails.database_arguments.should == { - :adapter => "mysql", - :log_level => "testlevel", - :host => "testserver", - :port => "9999", - :username => "testuser", - :password => "testpassword", - :database => "testname", - :socket => "testsocket", - :reconnect => true, - :pool => 1 - } - end -end - -describe Puppet::Rails, "when initializing a postgresql connection" do - confine "Cannot test without ActiveRecord" => Puppet.features.rails? - - it "should provide the adapter, log_level, and host, port, username, password, connections, and database arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("postgresql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("9999") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("") - Puppet.settings.stubs(:value).with(:dbconnections).returns(1) - - Puppet::Rails.database_arguments.should == { - :adapter => "postgresql", - :log_level => "testlevel", - :host => "testserver", - :port => "9999", - :username => "testuser", - :password => "testpassword", - :database => "testname", - :reconnect => true, - :pool => 1 - } - end - - it "should provide the adapter, log_level, and host, port, username, password, database, connections, and socket arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("postgresql") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") - Puppet.settings.stubs(:value).with(:dbport).returns("9999") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") - Puppet.settings.stubs(:value).with(:dbconnections).returns(1) - - Puppet::Rails.database_arguments.should == { - :adapter => "postgresql", - :log_level => "testlevel", - :host => "testserver", - :port => "9999", - :username => "testuser", - :password => "testpassword", - :database => "testname", - :socket => "testsocket", - :pool => 1, - :reconnect => true - } - end -end - -describe Puppet::Rails, "when initializing an Oracle connection" do - confine "Cannot test without ActiveRecord" => Puppet.features.rails? - - it "should provide the adapter, log_level, and username, password, dbconnections, and database arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("oracle_enhanced") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbconnections).returns(1) - - Puppet::Rails.database_arguments.should == { - :adapter => "oracle_enhanced", - :log_level => "testlevel", - :username => "testuser", - :password => "testpassword", - :database => "testname", - :pool => 1 - } - end - - it "should provide the adapter, log_level, and host, username, password, database, pool, and socket arguments" do - Puppet.settings.stubs(:value).with(:dbadapter).returns("oracle_enhanced") - Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") - Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") - Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") - Puppet.settings.stubs(:value).with(:dbname).returns("testname") - Puppet.settings.stubs(:value).with(:dbconnections).returns(1) - - Puppet::Rails.database_arguments.should == { - :adapter => "oracle_enhanced", - :log_level => "testlevel", - :username => "testuser", - :password => "testpassword", - :database => "testname", - :pool => 1 - } - end -end diff --git a/spec/unit/relationship_spec.rb b/spec/unit/relationship_spec.rb new file mode 100755 index 000000000..b98e4e26e --- /dev/null +++ b/spec/unit/relationship_spec.rb @@ -0,0 +1,236 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-11-1. +# Copyright (c) 2006. All rights reserved. + +require File.dirname(__FILE__) + '/../spec_helper' +require 'puppet/relationship' + +describe Puppet::Relationship do + before do + @edge = Puppet::Relationship.new(:a, :b) + end + + it "should have a :source attribute" do + @edge.should respond_to(:source) + end + + it "should have a :target attribute" do + @edge.should respond_to(:target) + end + + it "should have a :callback attribute" do + @edge.callback = :foo + @edge.callback.should == :foo + end + + it "should have an :event attribute" do + @edge.event = :NONE + @edge.event.should == :NONE + end + + it "should require a callback if a non-NONE event is specified" do + proc { @edge.event = :something }.should raise_error(ArgumentError) + end + + it "should have a :label attribute" do + @edge.should respond_to(:label) + end + + it "should provide a :ref method that describes the edge" do + @edge = Puppet::Relationship.new("a", "b") + @edge.ref.should == "a => b" + end + + it "should be able to produce a label as a hash with its event and callback" do + @edge.callback = :foo + @edge.event = :bar + + @edge.label.should == {:callback => :foo, :event => :bar} + end + + it "should work if nil options are provided" do + lambda { Puppet::Relationship.new("a", "b", nil) }.should_not raise_error + end +end + +describe Puppet::Relationship, " when initializing" do + before do + @edge = Puppet::Relationship.new(:a, :b) + end + + it "should use the first argument as the source" do + @edge.source.should == :a + end + + it "should use the second argument as the target" do + @edge.target.should == :b + end + + it "should set the rest of the arguments as the event and callback" do + @edge = Puppet::Relationship.new(:a, :b, :callback => :foo, :event => :bar) + @edge.callback.should == :foo + @edge.event.should == :bar + end + + it "should accept events specified as strings" do + @edge = Puppet::Relationship.new(:a, :b, "event" => :NONE) + @edge.event.should == :NONE + end + + it "should accept callbacks specified as strings" do + @edge = Puppet::Relationship.new(:a, :b, "callback" => :foo) + @edge.callback.should == :foo + end +end + +describe Puppet::Relationship, " when matching edges with no specified event" do + before do + @edge = Puppet::Relationship.new(:a, :b) + end + + it "should not match :NONE" do + @edge.should_not be_match(:NONE) + end + + it "should not match :ALL_EVENTS" do + @edge.should_not be_match(:NONE) + end + + it "should not match any other events" do + @edge.should_not be_match(:whatever) + end +end + +describe Puppet::Relationship, " when matching edges with :NONE as the event" do + before do + @edge = Puppet::Relationship.new(:a, :b, :event => :NONE) + end + it "should not match :NONE" do + @edge.should_not be_match(:NONE) + end + + it "should not match :ALL_EVENTS" do + @edge.should_not be_match(:ALL_EVENTS) + end + + it "should not match other events" do + @edge.should_not be_match(:yayness) + end +end + +describe Puppet::Relationship, " when matching edges with :ALL as the event" do + before do + @edge = Puppet::Relationship.new(:a, :b, :event => :ALL_EVENTS, :callback => :whatever) + end + + it "should not match :NONE" do + @edge.should_not be_match(:NONE) + end + + it "should match :ALL_EVENTS" do + @edge.should be_match(:ALLEVENTS) + end + + it "should match all other events" do + @edge.should be_match(:foo) + end +end + +describe Puppet::Relationship, " when matching edges with a non-standard event" do + before do + @edge = Puppet::Relationship.new(:a, :b, :event => :random, :callback => :whatever) + end + + it "should not match :NONE" do + @edge.should_not be_match(:NONE) + end + + it "should not match :ALL_EVENTS" do + @edge.should_not be_match(:ALL_EVENTS) + end + + it "should match events with the same name" do + @edge.should be_match(:random) + end +end + +describe Puppet::Relationship, "when converting to pson" do + confine "Missing 'pson' library" => Puppet.features.pson? + + before do + @edge = Puppet::Relationship.new(:a, :b, :event => :random, :callback => :whatever) + end + + it "should store the stringified source as the source in the data" do + PSON.parse(@edge.to_pson)["source"].should == "a" + end + + it "should store the stringified target as the target in the data" do + PSON.parse(@edge.to_pson)['target'].should == "b" + end + + it "should store the psonified event as the event in the data" do + PSON.parse(@edge.to_pson)["event"].should == "random" + end + + it "should not store an event when none is set" do + @edge.event = nil + PSON.parse(@edge.to_pson)["event"].should be_nil + end + + it "should store the psonified callback as the callback in the data" do + @edge.callback = "whatever" + PSON.parse(@edge.to_pson)["callback"].should == "whatever" + end + + it "should not store a callback when none is set in the edge" do + @edge.callback = nil + PSON.parse(@edge.to_pson)["callback"].should be_nil + end +end + +describe Puppet::Relationship, "when converting from pson" do + confine "Missing 'pson' library" => Puppet.features.pson? + + before do + @event = "random" + @callback = "whatever" + @data = { + "source" => "mysource", + "target" => "mytarget", + "event" => @event, + "callback" => @callback + } + @pson = { + "type" => "Puppet::Relationship", + "data" => @data + } + end + + def pson_result_should + Puppet::Relationship.expects(:new).with { |*args| yield args } + end + + it "should be extended with the PSON utility module" do + Puppet::Relationship.singleton_class.ancestors.should be_include(Puppet::Util::Pson) + end + + # LAK:NOTE For all of these tests, we convert back to the edge so we can + # trap the actual data structure then. + it "should pass the source in as the first argument" do + Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget").source.should == "mysource" + end + + it "should pass the target in as the second argument" do + Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget").target.should == "mytarget" + end + + it "should pass the event as an argument if it's provided" do + Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget", "event" => "myevent", "callback" => "eh").event.should == "myevent" + end + + it "should pass the callback as an argument if it's provided" do + Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget", "callback" => "mycallback").callback.should == "mycallback" + end +end diff --git a/spec/unit/relationship_spec_spec.rb b/spec/unit/relationship_spec_spec.rb deleted file mode 100755 index b98e4e26e..000000000 --- a/spec/unit/relationship_spec_spec.rb +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-11-1. -# Copyright (c) 2006. All rights reserved. - -require File.dirname(__FILE__) + '/../spec_helper' -require 'puppet/relationship' - -describe Puppet::Relationship do - before do - @edge = Puppet::Relationship.new(:a, :b) - end - - it "should have a :source attribute" do - @edge.should respond_to(:source) - end - - it "should have a :target attribute" do - @edge.should respond_to(:target) - end - - it "should have a :callback attribute" do - @edge.callback = :foo - @edge.callback.should == :foo - end - - it "should have an :event attribute" do - @edge.event = :NONE - @edge.event.should == :NONE - end - - it "should require a callback if a non-NONE event is specified" do - proc { @edge.event = :something }.should raise_error(ArgumentError) - end - - it "should have a :label attribute" do - @edge.should respond_to(:label) - end - - it "should provide a :ref method that describes the edge" do - @edge = Puppet::Relationship.new("a", "b") - @edge.ref.should == "a => b" - end - - it "should be able to produce a label as a hash with its event and callback" do - @edge.callback = :foo - @edge.event = :bar - - @edge.label.should == {:callback => :foo, :event => :bar} - end - - it "should work if nil options are provided" do - lambda { Puppet::Relationship.new("a", "b", nil) }.should_not raise_error - end -end - -describe Puppet::Relationship, " when initializing" do - before do - @edge = Puppet::Relationship.new(:a, :b) - end - - it "should use the first argument as the source" do - @edge.source.should == :a - end - - it "should use the second argument as the target" do - @edge.target.should == :b - end - - it "should set the rest of the arguments as the event and callback" do - @edge = Puppet::Relationship.new(:a, :b, :callback => :foo, :event => :bar) - @edge.callback.should == :foo - @edge.event.should == :bar - end - - it "should accept events specified as strings" do - @edge = Puppet::Relationship.new(:a, :b, "event" => :NONE) - @edge.event.should == :NONE - end - - it "should accept callbacks specified as strings" do - @edge = Puppet::Relationship.new(:a, :b, "callback" => :foo) - @edge.callback.should == :foo - end -end - -describe Puppet::Relationship, " when matching edges with no specified event" do - before do - @edge = Puppet::Relationship.new(:a, :b) - end - - it "should not match :NONE" do - @edge.should_not be_match(:NONE) - end - - it "should not match :ALL_EVENTS" do - @edge.should_not be_match(:NONE) - end - - it "should not match any other events" do - @edge.should_not be_match(:whatever) - end -end - -describe Puppet::Relationship, " when matching edges with :NONE as the event" do - before do - @edge = Puppet::Relationship.new(:a, :b, :event => :NONE) - end - it "should not match :NONE" do - @edge.should_not be_match(:NONE) - end - - it "should not match :ALL_EVENTS" do - @edge.should_not be_match(:ALL_EVENTS) - end - - it "should not match other events" do - @edge.should_not be_match(:yayness) - end -end - -describe Puppet::Relationship, " when matching edges with :ALL as the event" do - before do - @edge = Puppet::Relationship.new(:a, :b, :event => :ALL_EVENTS, :callback => :whatever) - end - - it "should not match :NONE" do - @edge.should_not be_match(:NONE) - end - - it "should match :ALL_EVENTS" do - @edge.should be_match(:ALLEVENTS) - end - - it "should match all other events" do - @edge.should be_match(:foo) - end -end - -describe Puppet::Relationship, " when matching edges with a non-standard event" do - before do - @edge = Puppet::Relationship.new(:a, :b, :event => :random, :callback => :whatever) - end - - it "should not match :NONE" do - @edge.should_not be_match(:NONE) - end - - it "should not match :ALL_EVENTS" do - @edge.should_not be_match(:ALL_EVENTS) - end - - it "should match events with the same name" do - @edge.should be_match(:random) - end -end - -describe Puppet::Relationship, "when converting to pson" do - confine "Missing 'pson' library" => Puppet.features.pson? - - before do - @edge = Puppet::Relationship.new(:a, :b, :event => :random, :callback => :whatever) - end - - it "should store the stringified source as the source in the data" do - PSON.parse(@edge.to_pson)["source"].should == "a" - end - - it "should store the stringified target as the target in the data" do - PSON.parse(@edge.to_pson)['target'].should == "b" - end - - it "should store the psonified event as the event in the data" do - PSON.parse(@edge.to_pson)["event"].should == "random" - end - - it "should not store an event when none is set" do - @edge.event = nil - PSON.parse(@edge.to_pson)["event"].should be_nil - end - - it "should store the psonified callback as the callback in the data" do - @edge.callback = "whatever" - PSON.parse(@edge.to_pson)["callback"].should == "whatever" - end - - it "should not store a callback when none is set in the edge" do - @edge.callback = nil - PSON.parse(@edge.to_pson)["callback"].should be_nil - end -end - -describe Puppet::Relationship, "when converting from pson" do - confine "Missing 'pson' library" => Puppet.features.pson? - - before do - @event = "random" - @callback = "whatever" - @data = { - "source" => "mysource", - "target" => "mytarget", - "event" => @event, - "callback" => @callback - } - @pson = { - "type" => "Puppet::Relationship", - "data" => @data - } - end - - def pson_result_should - Puppet::Relationship.expects(:new).with { |*args| yield args } - end - - it "should be extended with the PSON utility module" do - Puppet::Relationship.singleton_class.ancestors.should be_include(Puppet::Util::Pson) - end - - # LAK:NOTE For all of these tests, we convert back to the edge so we can - # trap the actual data structure then. - it "should pass the source in as the first argument" do - Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget").source.should == "mysource" - end - - it "should pass the target in as the second argument" do - Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget").target.should == "mytarget" - end - - it "should pass the event as an argument if it's provided" do - Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget", "event" => "myevent", "callback" => "eh").event.should == "myevent" - end - - it "should pass the callback as an argument if it's provided" do - Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget", "callback" => "mycallback").callback.should == "mycallback" - end -end diff --git a/spec/unit/reports/http_spec.rb b/spec/unit/reports/http_spec.rb new file mode 100644 index 000000000..cc7346467 --- /dev/null +++ b/spec/unit/reports/http_spec.rb @@ -0,0 +1,56 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/reports' + +# FakeHTTP fakes the behavior of Net::HTTP#request and acts as a sensor for an +# otherwise difficult to trace method call. +# +class FakeHTTP + REQUESTS = {} + def self.request(req) + REQUESTS[req.path] = req + end +end + +processor = Puppet::Reports.report(:http) + +describe processor do + before { Net::HTTP.any_instance.stubs(:start).yields(FakeHTTP) } + subject { Puppet::Transaction::Report.new.extend(processor) } + + it { should respond_to(:process) } + + it "should use the reporturl setting's host and port" do + uri = URI.parse(Puppet[:reporturl]) + Net::HTTP.expects(:new).with(uri.host, uri.port).returns(stub_everything('http')) + subject.process + end + + describe "request" do + before { subject.process } + + describe "path" do + it "should use the path specified by the 'reporturl' setting" do + reports_request.path.should == URI.parse(Puppet[:reporturl]).path + end + end + + describe "body" do + it "should be the report as YAML" do + reports_request.body.should == subject.to_yaml + end + end + + describe "content type" do + it "should be 'application/x-yaml'" do + reports_request.content_type.should == "application/x-yaml" + end + end + end + + private + + def reports_request; FakeHTTP::REQUESTS[URI.parse(Puppet[:reporturl]).path] end +end diff --git a/spec/unit/reports/http_spec_spec.rb b/spec/unit/reports/http_spec_spec.rb deleted file mode 100644 index cc7346467..000000000 --- a/spec/unit/reports/http_spec_spec.rb +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/reports' - -# FakeHTTP fakes the behavior of Net::HTTP#request and acts as a sensor for an -# otherwise difficult to trace method call. -# -class FakeHTTP - REQUESTS = {} - def self.request(req) - REQUESTS[req.path] = req - end -end - -processor = Puppet::Reports.report(:http) - -describe processor do - before { Net::HTTP.any_instance.stubs(:start).yields(FakeHTTP) } - subject { Puppet::Transaction::Report.new.extend(processor) } - - it { should respond_to(:process) } - - it "should use the reporturl setting's host and port" do - uri = URI.parse(Puppet[:reporturl]) - Net::HTTP.expects(:new).with(uri.host, uri.port).returns(stub_everything('http')) - subject.process - end - - describe "request" do - before { subject.process } - - describe "path" do - it "should use the path specified by the 'reporturl' setting" do - reports_request.path.should == URI.parse(Puppet[:reporturl]).path - end - end - - describe "body" do - it "should be the report as YAML" do - reports_request.body.should == subject.to_yaml - end - end - - describe "content type" do - it "should be 'application/x-yaml'" do - reports_request.content_type.should == "application/x-yaml" - end - end - end - - private - - def reports_request; FakeHTTP::REQUESTS[URI.parse(Puppet[:reporturl]).path] end -end diff --git a/spec/unit/reports/tagmail_spec.rb b/spec/unit/reports/tagmail_spec.rb new file mode 100755 index 000000000..87b070bf4 --- /dev/null +++ b/spec/unit/reports/tagmail_spec.rb @@ -0,0 +1,94 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/reports' +require 'puppettest' + +tagmail = Puppet::Reports.report(:tagmail) + +describe tagmail do + extend PuppetTest + + before do + @processor = Puppet::Transaction::Report.new + @processor.extend(Puppet::Reports.report(:tagmail)) + end + + passers = File.join(datadir, "reports", "tagmail_passers.conf") + File.readlines(passers).each do |line| + it "should be able to parse '#{line.inspect}'" do + @processor.parse(line) + end + end + + failers = File.join(datadir, "reports", "tagmail_failers.conf") + File.readlines(failers).each do |line| + it "should not be able to parse '#{line.inspect}'" do + lambda { @processor.parse(line) }.should raise_error(ArgumentError) + end + end + + { + "tag: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag}, []], + "tag, other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag other}, []], + "tag-other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag-other}, []], + "tag, !other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag}, %w{other}], + "tag, !other, one, !two: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag one}, %w{other two}], + "tag: abuse@domain.com, other@domain.com" => [%w{abuse@domain.com other@domain.com}, %w{tag}, []] + + }.each do |line, results| + it "should parse '#{line}' as #{results.inspect}" do + @processor.parse(line).shift.should == results + end + end + + describe "when matching logs" do + before do + @processor << Puppet::Util::Log.new(:level => :notice, :message => "first", :tags => %w{one}) + @processor << Puppet::Util::Log.new(:level => :notice, :message => "second", :tags => %w{one two}) + @processor << Puppet::Util::Log.new(:level => :notice, :message => "third", :tags => %w{one two three}) + end + + def match(pos = [], neg = []) + pos = Array(pos) + neg = Array(neg) + result = @processor.match([[%w{abuse@domain.com}, pos, neg]]) + actual_result = result.shift + if actual_result + actual_result[1] + else + nil + end + end + + it "should match all messages when provided the 'all' tag as a positive matcher" do + results = match("all") + %w{first second third}.each do |str| + results.should be_include(str) + end + end + + it "should remove messages that match a negated tag" do + match("all", "three").should_not be_include("third") + end + + it "should find any messages tagged with a provided tag" do + results = match("two") + results.should be_include("second") + results.should be_include("third") + results.should_not be_include("first") + end + + it "should allow negation of specific tags from a specific tag list" do + results = match("two", "three") + results.should be_include("second") + results.should_not be_include("third") + end + + it "should allow a tag to negate all matches" do + results = match([], "one") + results.should be_nil + end + end +end diff --git a/spec/unit/reports/tagmail_spec_spec.rb b/spec/unit/reports/tagmail_spec_spec.rb deleted file mode 100755 index 87b070bf4..000000000 --- a/spec/unit/reports/tagmail_spec_spec.rb +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/reports' -require 'puppettest' - -tagmail = Puppet::Reports.report(:tagmail) - -describe tagmail do - extend PuppetTest - - before do - @processor = Puppet::Transaction::Report.new - @processor.extend(Puppet::Reports.report(:tagmail)) - end - - passers = File.join(datadir, "reports", "tagmail_passers.conf") - File.readlines(passers).each do |line| - it "should be able to parse '#{line.inspect}'" do - @processor.parse(line) - end - end - - failers = File.join(datadir, "reports", "tagmail_failers.conf") - File.readlines(failers).each do |line| - it "should not be able to parse '#{line.inspect}'" do - lambda { @processor.parse(line) }.should raise_error(ArgumentError) - end - end - - { - "tag: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag}, []], - "tag, other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag other}, []], - "tag-other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag-other}, []], - "tag, !other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag}, %w{other}], - "tag, !other, one, !two: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag one}, %w{other two}], - "tag: abuse@domain.com, other@domain.com" => [%w{abuse@domain.com other@domain.com}, %w{tag}, []] - - }.each do |line, results| - it "should parse '#{line}' as #{results.inspect}" do - @processor.parse(line).shift.should == results - end - end - - describe "when matching logs" do - before do - @processor << Puppet::Util::Log.new(:level => :notice, :message => "first", :tags => %w{one}) - @processor << Puppet::Util::Log.new(:level => :notice, :message => "second", :tags => %w{one two}) - @processor << Puppet::Util::Log.new(:level => :notice, :message => "third", :tags => %w{one two three}) - end - - def match(pos = [], neg = []) - pos = Array(pos) - neg = Array(neg) - result = @processor.match([[%w{abuse@domain.com}, pos, neg]]) - actual_result = result.shift - if actual_result - actual_result[1] - else - nil - end - end - - it "should match all messages when provided the 'all' tag as a positive matcher" do - results = match("all") - %w{first second third}.each do |str| - results.should be_include(str) - end - end - - it "should remove messages that match a negated tag" do - match("all", "three").should_not be_include("third") - end - - it "should find any messages tagged with a provided tag" do - results = match("two") - results.should be_include("second") - results.should be_include("third") - results.should_not be_include("first") - end - - it "should allow negation of specific tags from a specific tag list" do - results = match("two", "three") - results.should be_include("second") - results.should_not be_include("third") - end - - it "should allow a tag to negate all matches" do - results = match([], "one") - results.should be_nil - end - end -end diff --git a/spec/unit/reports_spec.rb b/spec/unit/reports_spec.rb new file mode 100755 index 000000000..f12f0d717 --- /dev/null +++ b/spec/unit/reports_spec.rb @@ -0,0 +1,61 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'puppet/reports' + +describe Puppet::Reports do + it "should instance-load report types" do + Puppet::Reports.instance_loader(:report).should be_instance_of(Puppet::Util::Autoload) + end + + it "should have a method for registering report types" do + Puppet::Reports.should respond_to(:register_report) + end + + it "should have a method for retrieving report types by name" do + Puppet::Reports.should respond_to(:report) + end + + it "should provide a method for returning documentation for all reports" do + Puppet::Reports.expects(:loaded_instances).with(:report).returns([:one, :two]) + one = mock 'one', :doc => "onedoc" + two = mock 'two', :doc => "twodoc" + Puppet::Reports.expects(:report).with(:one).returns(one) + Puppet::Reports.expects(:report).with(:two).returns(two) + + doc = Puppet::Reports.reportdocs + doc.include?("onedoc").should be_true + doc.include?("twodoc").should be_true + end +end + + +describe Puppet::Reports, " when loading report types" do + it "should use the instance loader to retrieve report types" do + Puppet::Reports.expects(:loaded_instance).with(:report, :myreporttype) + Puppet::Reports.report(:myreporttype) + end +end + +describe Puppet::Reports, " when registering report types" do + it "should evaluate the supplied block as code for a module" do + Puppet::Reports.expects(:genmodule).returns(Module.new) + Puppet::Reports.register_report(:testing) { } + end + + it "should extend the report type with the Puppet::Util::Docs module" do + mod = stub 'module', :define_method => true + + Puppet::Reports.expects(:genmodule).with { |name, options, block| options[:extend] == Puppet::Util::Docs }.returns(mod) + Puppet::Reports.register_report(:testing) { } + end + + it "should define a :report_name method in the module that returns the name of the report" do + mod = mock 'module' + mod.expects(:define_method).with(:report_name) + + Puppet::Reports.expects(:genmodule).returns(mod) + Puppet::Reports.register_report(:testing) { } + end +end diff --git a/spec/unit/reports_spec_spec.rb b/spec/unit/reports_spec_spec.rb deleted file mode 100755 index f12f0d717..000000000 --- a/spec/unit/reports_spec_spec.rb +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -require 'puppet/reports' - -describe Puppet::Reports do - it "should instance-load report types" do - Puppet::Reports.instance_loader(:report).should be_instance_of(Puppet::Util::Autoload) - end - - it "should have a method for registering report types" do - Puppet::Reports.should respond_to(:register_report) - end - - it "should have a method for retrieving report types by name" do - Puppet::Reports.should respond_to(:report) - end - - it "should provide a method for returning documentation for all reports" do - Puppet::Reports.expects(:loaded_instances).with(:report).returns([:one, :two]) - one = mock 'one', :doc => "onedoc" - two = mock 'two', :doc => "twodoc" - Puppet::Reports.expects(:report).with(:one).returns(one) - Puppet::Reports.expects(:report).with(:two).returns(two) - - doc = Puppet::Reports.reportdocs - doc.include?("onedoc").should be_true - doc.include?("twodoc").should be_true - end -end - - -describe Puppet::Reports, " when loading report types" do - it "should use the instance loader to retrieve report types" do - Puppet::Reports.expects(:loaded_instance).with(:report, :myreporttype) - Puppet::Reports.report(:myreporttype) - end -end - -describe Puppet::Reports, " when registering report types" do - it "should evaluate the supplied block as code for a module" do - Puppet::Reports.expects(:genmodule).returns(Module.new) - Puppet::Reports.register_report(:testing) { } - end - - it "should extend the report type with the Puppet::Util::Docs module" do - mod = stub 'module', :define_method => true - - Puppet::Reports.expects(:genmodule).with { |name, options, block| options[:extend] == Puppet::Util::Docs }.returns(mod) - Puppet::Reports.register_report(:testing) { } - end - - it "should define a :report_name method in the module that returns the name of the report" do - mod = mock 'module' - mod.expects(:define_method).with(:report_name) - - Puppet::Reports.expects(:genmodule).returns(mod) - Puppet::Reports.register_report(:testing) { } - end -end diff --git a/spec/unit/resource/catalog_spec.rb b/spec/unit/resource/catalog_spec.rb new file mode 100755 index 000000000..e633b131c --- /dev/null +++ b/spec/unit/resource/catalog_spec.rb @@ -0,0 +1,1067 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Resource::Catalog, "when compiling" do + + before do + @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" + end + + it "should be an Expirer" do + Puppet::Resource::Catalog.ancestors.should be_include(Puppet::Util::Cacher::Expirer) + end + + it "should always be expired if it's not applying" do + @catalog = Puppet::Resource::Catalog.new("host") + @catalog.expects(:applying?).returns false + @catalog.should be_dependent_data_expired(Time.now) + end + + it "should not be expired if it's applying and the timestamp is late enough" do + @catalog = Puppet::Resource::Catalog.new("host") + @catalog.expire + @catalog.expects(:applying?).returns true + @catalog.should_not be_dependent_data_expired(Time.now) + end + + it "should be able to write its list of classes to the class file" do + @catalog = Puppet::Resource::Catalog.new("host") + + @catalog.add_class "foo", "bar" + + Puppet.settings.expects(:value).with(:classfile).returns "/class/file" + + fh = mock 'filehandle' + File.expects(:open).with("/class/file", "w").yields fh + + fh.expects(:puts).with "foo\nbar" + + @catalog.write_class_file + end + + it "should have a client_version attribute" do + @catalog = Puppet::Resource::Catalog.new("host") + @catalog.client_version = 5 + @catalog.client_version.should == 5 + end + + it "should have a server_version attribute" do + @catalog = Puppet::Resource::Catalog.new("host") + @catalog.server_version = 5 + @catalog.server_version.should == 5 + end + + describe "when compiling" do + it "should accept tags" do + config = Puppet::Resource::Catalog.new("mynode") + config.tag("one") + config.tags.should == %w{one} + end + + it "should accept multiple tags at once" do + config = Puppet::Resource::Catalog.new("mynode") + config.tag("one", "two") + config.tags.should == %w{one two} + end + + it "should convert all tags to strings" do + config = Puppet::Resource::Catalog.new("mynode") + config.tag("one", :two) + config.tags.should == %w{one two} + end + + it "should tag with both the qualified name and the split name" do + config = Puppet::Resource::Catalog.new("mynode") + config.tag("one::two") + config.tags.include?("one").should be_true + config.tags.include?("one::two").should be_true + end + + it "should accept classes" do + config = Puppet::Resource::Catalog.new("mynode") + config.add_class("one") + config.classes.should == %w{one} + config.add_class("two", "three") + config.classes.should == %w{one two three} + end + + it "should tag itself with passed class names" do + config = Puppet::Resource::Catalog.new("mynode") + config.add_class("one") + config.tags.should == %w{one} + end + end + + describe "when extracting transobjects" do + + def mkscope + @node = Puppet::Node.new("mynode") + @compiler = Puppet::Parser::Compiler.new(@node) + + # XXX This is ridiculous. + @compiler.send(:evaluate_main) + @scope = @compiler.topscope + end + + def mkresource(type, name) + Puppet::Parser::Resource.new(type, name, :source => @source, :scope => @scope) + end + + it "should fail if no 'main' stage can be found" do + lambda { Puppet::Resource::Catalog.new("mynode").extract }.should raise_error(Puppet::DevError) + end + + it "should warn if any non-main stages are present" do + config = Puppet::Resource::Catalog.new("mynode") + + @scope = mkscope + @source = mock 'source' + + main = mkresource("stage", "main") + config.add_resource(main) + + other = mkresource("stage", "other") + config.add_resource(other) + + Puppet.expects(:warning) + + config.extract + end + + it "should always create a TransBucket for the 'main' stage" do + config = Puppet::Resource::Catalog.new("mynode") + + @scope = mkscope + @source = mock 'source' + + main = mkresource("stage", "main") + config.add_resource(main) + + result = config.extract + result.type.should == "Stage" + result.name.should == "main" + end + + # Now try it with a more complicated graph -- a three tier graph, each tier + it "should transform arbitrarily deep graphs into isomorphic trees" do + config = Puppet::Resource::Catalog.new("mynode") + + @scope = mkscope + @scope.stubs(:tags).returns([]) + @source = mock 'source' + + # Create our scopes. + top = mkresource "stage", "main" + + config.add_resource top + topbucket = [] + topbucket.expects(:classes=).with([]) + top.expects(:to_trans).returns(topbucket) + topres = mkresource "file", "/top" + topres.expects(:to_trans).returns(:topres) + config.add_edge top, topres + + middle = mkresource "class", "middle" + middle.expects(:to_trans).returns([]) + config.add_edge top, middle + midres = mkresource "file", "/mid" + midres.expects(:to_trans).returns(:midres) + config.add_edge middle, midres + + bottom = mkresource "class", "bottom" + bottom.expects(:to_trans).returns([]) + config.add_edge middle, bottom + botres = mkresource "file", "/bot" + botres.expects(:to_trans).returns(:botres) + config.add_edge bottom, botres + + toparray = config.extract + + # This is annoying; it should look like: + # [[[:botres], :midres], :topres] + # but we can't guarantee sort order. + toparray.include?(:topres).should be_true + + midarray = toparray.find { |t| t.is_a?(Array) } + midarray.include?(:midres).should be_true + botarray = midarray.find { |t| t.is_a?(Array) } + botarray.include?(:botres).should be_true + end + end + + describe " when converting to a Puppet::Resource catalog" do + before do + @original = Puppet::Resource::Catalog.new("mynode") + @original.tag(*%w{one two three}) + @original.add_class *%w{four five six} + + @top = Puppet::TransObject.new 'top', "class" + @topobject = Puppet::TransObject.new '/topobject', "file" + @middle = Puppet::TransObject.new 'middle', "class" + @middleobject = Puppet::TransObject.new '/middleobject', "file" + @bottom = Puppet::TransObject.new 'bottom', "class" + @bottomobject = Puppet::TransObject.new '/bottomobject', "file" + + @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] + + @original.add_resource(*@resources) + + @original.add_edge(@top, @topobject) + @original.add_edge(@top, @middle) + @original.add_edge(@middle, @middleobject) + @original.add_edge(@middle, @bottom) + @original.add_edge(@bottom, @bottomobject) + + @catalog = @original.to_resource + end + + it "should copy over the version" do + @original.version = "foo" + @original.to_resource.version.should == "foo" + end + + it "should convert parser resources to plain resources" do + resource = Puppet::Parser::Resource.new(:file, "foo", :scope => stub("scope"), :source => stub("source")) + catalog = Puppet::Resource::Catalog.new("whev") + catalog.add_resource(resource) + new = catalog.to_resource + new.resource(:file, "foo").class.should == Puppet::Resource + end + + it "should add all resources as Puppet::Resource instances" do + @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Resource) } + end + + it "should copy the tag list to the new catalog" do + @catalog.tags.sort.should == @original.tags.sort + end + + it "should copy the class list to the new catalog" do + @catalog.classes.should == @original.classes + end + + it "should duplicate the original edges" do + @original.edges.each do |edge| + @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true + end + end + + it "should set itself as the catalog for each converted resource" do + @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } + end + end + + describe "when converting to a RAL catalog" do + before do + @original = Puppet::Resource::Catalog.new("mynode") + @original.tag(*%w{one two three}) + @original.add_class *%w{four five six} + + @top = Puppet::Resource.new :class, 'top' + @topobject = Puppet::Resource.new :file, @basepath+'/topobject' + @middle = Puppet::Resource.new :class, 'middle' + @middleobject = Puppet::Resource.new :file, @basepath+'/middleobject' + @bottom = Puppet::Resource.new :class, 'bottom' + @bottomobject = Puppet::Resource.new :file, @basepath+'/bottomobject' + + @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] + + @original.add_resource(*@resources) + + @original.add_edge(@top, @topobject) + @original.add_edge(@top, @middle) + @original.add_edge(@middle, @middleobject) + @original.add_edge(@middle, @bottom) + @original.add_edge(@bottom, @bottomobject) + + @catalog = @original.to_ral + end + + it "should add all resources as RAL instances" do + @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Type) } + end + + it "should copy the tag list to the new catalog" do + @catalog.tags.sort.should == @original.tags.sort + end + + it "should copy the class list to the new catalog" do + @catalog.classes.should == @original.classes + end + + it "should duplicate the original edges" do + @original.edges.each do |edge| + @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true + end + end + + it "should set itself as the catalog for each converted resource" do + @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } + end + + # This tests #931. + it "should not lose track of resources whose names vary" do + changer = Puppet::TransObject.new 'changer', 'test' + + config = Puppet::Resource::Catalog.new('test') + config.add_resource(changer) + config.add_resource(@top) + + config.add_edge(@top, changer) + + resource = stub 'resource', :name => "changer2", :title => "changer2", :ref => "Test[changer2]", :catalog= => nil, :remove => nil + + #changer is going to get duplicated as part of a fix for aliases 1094 + changer.expects(:dup).returns(changer) + changer.expects(:to_ral).returns(resource) + + newconfig = nil + + proc { @catalog = config.to_ral }.should_not raise_error + @catalog.resource("Test[changer2]").should equal(resource) + end + + after do + # Remove all resource instances. + @catalog.clear(true) + end + end + + describe "when filtering" do + before :each do + @original = Puppet::Resource::Catalog.new("mynode") + @original.tag(*%w{one two three}) + @original.add_class *%w{four five six} + + @r1 = stub_everything 'r1', :ref => "File[/a]" + @r1.stubs(:respond_to?).with(:ref).returns(true) + @r1.stubs(:dup).returns(@r1) + @r1.stubs(:is_a?).returns(Puppet::Resource).returns(true) + + @r2 = stub_everything 'r2', :ref => "File[/b]" + @r2.stubs(:respond_to?).with(:ref).returns(true) + @r2.stubs(:dup).returns(@r2) + @r2.stubs(:is_a?).returns(Puppet::Resource).returns(true) + + @resources = [@r1,@r2] + + @original.add_resource(@r1,@r2) + end + + it "should transform the catalog to a resource catalog" do + @original.expects(:to_catalog).with { |h,b| h == :to_resource } + + @original.filter + end + + it "should scan each catalog resource in turn and apply filtering block" do + @resources.each { |r| r.expects(:test?) } + @original.filter do |r| + r.test? + end + end + + it "should filter out resources which produce true when the filter block is evaluated" do + @original.filter do |r| + r == @r1 + end.resource("File[/a]").should be_nil + end + + it "should not consider edges against resources that were filtered out" do + @original.add_edge(@r1,@r2) + @original.filter do |r| + r == @r1 + end.edge(@r1,@r2).should be_empty + end + end + + describe "when functioning as a resource container" do + before do + @catalog = Puppet::Resource::Catalog.new("host") + @one = Puppet::Type.type(:notify).new :name => "one" + @two = Puppet::Type.type(:notify).new :name => "two" + @dupe = Puppet::Type.type(:notify).new :name => "one" + end + + it "should provide a method to add one or more resources" do + @catalog.add_resource @one, @two + @catalog.resource(@one.ref).should equal(@one) + @catalog.resource(@two.ref).should equal(@two) + end + + it "should add resources to the relationship graph if it exists" do + relgraph = @catalog.relationship_graph + @catalog.add_resource @one + relgraph.should be_vertex(@one) + end + + it "should yield added resources if a block is provided" do + yielded = [] + @catalog.add_resource(@one, @two) { |r| yielded << r } + yielded.length.should == 2 + end + + it "should set itself as the resource's catalog if it is not a relationship graph" do + @one.expects(:catalog=).with(@catalog) + @catalog.add_resource @one + end + + it "should make all vertices available by resource reference" do + @catalog.add_resource(@one) + @catalog.resource(@one.ref).should equal(@one) + @catalog.vertices.find { |r| r.ref == @one.ref }.should equal(@one) + end + + it "should canonize how resources are referred to during retrieval when both type and title are provided" do + @catalog.add_resource(@one) + + @catalog.resource("notify", "one").should equal(@one) + end + + it "should canonize how resources are referred to during retrieval when just the title is provided" do + @catalog.add_resource(@one) + + @catalog.resource("notify[one]", nil).should equal(@one) + end + + it "should not allow two resources with the same resource reference" do + @catalog.add_resource(@one) + + proc { @catalog.add_resource(@dupe) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) + end + + it "should not store objects that do not respond to :ref" do + proc { @catalog.add_resource("thing") }.should raise_error(ArgumentError) + end + + it "should remove all resources when asked" do + @catalog.add_resource @one + @catalog.add_resource @two + @one.expects :remove + @two.expects :remove + @catalog.clear(true) + end + + it "should support a mechanism for finishing resources" do + @one.expects :finish + @two.expects :finish + @catalog.add_resource @one + @catalog.add_resource @two + + @catalog.finalize + end + + it "should make default resources when finalizing" do + @catalog.expects(:make_default_resources) + @catalog.finalize + end + + it "should add default resources to the catalog upon creation" do + @catalog.make_default_resources + @catalog.resource(:schedule, "daily").should_not be_nil + end + + it "should optionally support an initialization block and should finalize after such blocks" do + @one.expects :finish + @two.expects :finish + config = Puppet::Resource::Catalog.new("host") do |conf| + conf.add_resource @one + conf.add_resource @two + end + end + + it "should inform the resource that it is the resource's catalog" do + @one.expects(:catalog=).with(@catalog) + @catalog.add_resource @one + end + + it "should be able to find resources by reference" do + @catalog.add_resource @one + @catalog.resource(@one.ref).should equal(@one) + end + + it "should be able to find resources by reference or by type/title tuple" do + @catalog.add_resource @one + @catalog.resource("notify", "one").should equal(@one) + end + + it "should have a mechanism for removing resources" do + @catalog.add_resource @one + @one.expects :remove + @catalog.remove_resource(@one) + @catalog.resource(@one.ref).should be_nil + @catalog.vertex?(@one).should be_false + end + + it "should have a method for creating aliases for resources" do + @catalog.add_resource @one + @catalog.alias(@one, "other") + @catalog.resource("notify", "other").should equal(@one) + end + + it "should ignore conflicting aliases that point to the aliased resource" do + @catalog.alias(@one, "other") + lambda { @catalog.alias(@one, "other") }.should_not raise_error + end + + it "should create aliases for resources isomorphic resources whose names do not match their titles" do + resource = Puppet::Type::File.new(:title => "testing", :path => @basepath+"/something") + + @catalog.add_resource(resource) + + @catalog.resource(:file, @basepath+"/something").should equal(resource) + end + + it "should not create aliases for resources non-isomorphic resources whose names do not match their titles" do + resource = Puppet::Type.type(:exec).new(:title => "testing", :command => "echo", :path => %w{/bin /usr/bin /usr/local/bin}) + + @catalog.add_resource(resource) + + # Yay, I've already got a 'should' method + @catalog.resource(:exec, "echo").object_id.should == nil.object_id + end + + # This test is the same as the previous, but the behaviour should be explicit. + it "should alias using the class name from the resource reference, not the resource class name" do + @catalog.add_resource @one + @catalog.alias(@one, "other") + @catalog.resource("notify", "other").should equal(@one) + end + + it "should ignore conflicting aliases that point to the aliased resource" do + @catalog.alias(@one, "other") + lambda { @catalog.alias(@one, "other") }.should_not raise_error + end + + it "should fail to add an alias if the aliased name already exists" do + @catalog.add_resource @one + proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError) + end + + it "should not fail when a resource has duplicate aliases created" do + @catalog.add_resource @one + proc { @catalog.alias @one, "one" }.should_not raise_error + end + + it "should not create aliases that point back to the resource" do + @catalog.alias(@one, "one") + @catalog.resource(:notify, "one").should be_nil + end + + it "should be able to look resources up by their aliases" do + @catalog.add_resource @one + @catalog.alias @one, "two" + @catalog.resource(:notify, "two").should equal(@one) + end + + it "should remove resource aliases when the target resource is removed" do + @catalog.add_resource @one + @catalog.alias(@one, "other") + @one.expects :remove + @catalog.remove_resource(@one) + @catalog.resource("notify", "other").should be_nil + end + + it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do + resource = Puppet::Type.type(:file).new :path => @basepath+"/something", :title => "other", :content => "blah" + resource.expects(:isomorphic?).returns(true) + @catalog.add_resource(resource) + @catalog.resource(:file, "other").should equal(resource) + @catalog.resource(:file, @basepath+"/something").ref.should == resource.ref + end + + it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do + resource = Puppet::Type.type(:file).new :path => @basepath+"/something", :title => "other", :content => "blah" + resource.expects(:isomorphic?).returns(false) + @catalog.add_resource(resource) + @catalog.resource(:file, resource.title).should equal(resource) + # We can't use .should here, because the resources respond to that method. + if @catalog.resource(:file, resource.name) + raise "Aliased non-isomorphic resource" + end + end + + it "should provide a method to create additional resources that also registers the resource" do + args = {:name => "/yay", :ensure => :file} + resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" + Puppet::Type.type(:file).expects(:new).with(args).returns(resource) + @catalog.create_resource :file, args + @catalog.resource("File[/yay]").should equal(resource) + end + end + + describe "when applying" do + before :each do + @catalog = Puppet::Resource::Catalog.new("host") + + @transaction = mock 'transaction' + Puppet::Transaction.stubs(:new).returns(@transaction) + @transaction.stubs(:evaluate) + @transaction.stubs(:add_times) + end + + it "should create and evaluate a transaction" do + @transaction.expects(:evaluate) + @catalog.apply + end + + it "should provide the catalog retrieval time to the transaction" do + @catalog.retrieval_duration = 5 + @transaction.expects(:add_times).with(:config_retrieval => 5) + @catalog.apply + end + + it "should use a retrieval time of 0 if none is set in the catalog" do + @catalog.retrieval_duration = nil + @transaction.expects(:add_times).with(:config_retrieval => 0) + @catalog.apply + end + + it "should return the transaction" do + @catalog.apply.should equal(@transaction) + end + + it "should yield the transaction if a block is provided" do + @catalog.apply do |trans| + trans.should equal(@transaction) + end + end + + it "should default to being a host catalog" do + @catalog.host_config.should be_true + end + + it "should be able to be set to a non-host_config" do + @catalog.host_config = false + @catalog.host_config.should be_false + end + + it "should pass supplied tags on to the transaction" do + @transaction.expects(:tags=).with(%w{one two}) + @catalog.apply(:tags => %w{one two}) + end + + it "should set ignoreschedules on the transaction if specified in apply()" do + @transaction.expects(:ignoreschedules=).with(true) + @catalog.apply(:ignoreschedules => true) + end + + it "should expire cached data in the resources both before and after the transaction" do + @catalog.expects(:expire).times(2) + @catalog.apply + end + + describe "host catalogs" do + + # super() doesn't work in the setup method for some reason + before do + @catalog.host_config = true + Puppet::Util::Storage.stubs(:store) + end + + it "should initialize the state database before applying a catalog" do + Puppet::Util::Storage.expects(:load) + + # Short-circuit the apply, so we know we're loading before the transaction + Puppet::Transaction.expects(:new).raises ArgumentError + proc { @catalog.apply }.should raise_error(ArgumentError) + end + + it "should sync the state database after applying" do + Puppet::Util::Storage.expects(:store) + @transaction.stubs :any_failed? => false + @catalog.apply + end + + after { Puppet.settings.clear } + end + + describe "non-host catalogs" do + + before do + @catalog.host_config = false + end + + it "should never send reports" do + Puppet[:report] = true + Puppet[:summarize] = true + @catalog.apply + end + + it "should never modify the state database" do + Puppet::Util::Storage.expects(:load).never + Puppet::Util::Storage.expects(:store).never + @catalog.apply + end + + after { Puppet.settings.clear } + end + end + + describe "when creating a relationship graph" do + before do + Puppet::Type.type(:component) + @catalog = Puppet::Resource::Catalog.new("host") + @compone = Puppet::Type::Component.new :name => "one" + @comptwo = Puppet::Type::Component.new :name => "two", :require => "Class[one]" + @file = Puppet::Type.type(:file) + @one = @file.new :path => @basepath+"/one" + @two = @file.new :path => @basepath+"/two" + @sub = @file.new :path => @basepath+"/two/subdir" + @catalog.add_edge @compone, @one + @catalog.add_edge @comptwo, @two + + @three = @file.new :path => @basepath+"/three" + @four = @file.new :path => @basepath+"/four", :require => "File[#{@basepath}/three]" + @five = @file.new :path => @basepath+"/five" + @catalog.add_resource @compone, @comptwo, @one, @two, @three, @four, @five, @sub + + @relationships = @catalog.relationship_graph + end + + it "should be able to create a relationship graph" do + @relationships.should be_instance_of(Puppet::SimpleGraph) + end + + it "should not have any components" do + @relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil + end + + it "should have all non-component resources from the catalog" do + # The failures print out too much info, so i just do a class comparison + @relationships.vertex?(@five).should be_true + end + + it "should have all resource relationships set as edges" do + @relationships.edge?(@three, @four).should be_true + end + + it "should copy component relationships to all contained resources" do + @relationships.edge?(@one, @two).should be_true + end + + it "should add automatic relationships to the relationship graph" do + @relationships.edge?(@two, @sub).should be_true + end + + it "should get removed when the catalog is cleaned up" do + @relationships.expects(:clear) + @catalog.clear + @catalog.instance_variable_get("@relationship_graph").should be_nil + end + + it "should write :relationships and :expanded_relationships graph files if the catalog is a host catalog" do + @catalog.clear + graph = Puppet::SimpleGraph.new + Puppet::SimpleGraph.expects(:new).returns graph + + graph.expects(:write_graph).with(:relationships) + graph.expects(:write_graph).with(:expanded_relationships) + + @catalog.host_config = true + + @catalog.relationship_graph + end + + it "should not write graph files if the catalog is not a host catalog" do + @catalog.clear + graph = Puppet::SimpleGraph.new + Puppet::SimpleGraph.expects(:new).returns graph + + graph.expects(:write_graph).never + + @catalog.host_config = false + + @catalog.relationship_graph + end + + it "should create a new relationship graph after clearing the old one" do + @relationships.expects(:clear) + @catalog.clear + @catalog.relationship_graph.should be_instance_of(Puppet::SimpleGraph) + end + + it "should remove removed resources from the relationship graph if it exists" do + @catalog.remove_resource(@one) + @catalog.relationship_graph.vertex?(@one).should be_false + end + end + + describe "when writing dot files" do + before do + @catalog = Puppet::Resource::Catalog.new("host") + @name = :test + @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") + end + + it "should only write when it is a host catalog" do + File.expects(:open).with(@file).never + @catalog.host_config = false + Puppet[:graph] = true + @catalog.write_graph(@name) + end + + after do + Puppet.settings.clear + end + end + + describe "when indirecting" do + before do + @real_indirection = Puppet::Resource::Catalog.indirection + + @indirection = stub 'indirection', :name => :catalog + + Puppet::Util::Cacher.expire + end + + it "should redirect to the indirection for retrieval" do + Puppet::Resource::Catalog.stubs(:indirection).returns(@indirection) + @indirection.expects(:find) + Puppet::Resource::Catalog.find(:myconfig) + end + + it "should use the value of the 'catalog_terminus' setting to determine its terminus class" do + # Puppet only checks the terminus setting the first time you ask + # so this returns the object to the clean state + # at the expense of making this test less pure + Puppet::Resource::Catalog.indirection.reset_terminus_class + + Puppet.settings[:catalog_terminus] = "rest" + Puppet::Resource::Catalog.indirection.terminus_class.should == :rest + end + + it "should allow the terminus class to be set manually" do + Puppet::Resource::Catalog.indirection.terminus_class = :rest + Puppet::Resource::Catalog.indirection.terminus_class.should == :rest + end + + after do + Puppet::Util::Cacher.expire + @real_indirection.reset_terminus_class + end + end + + describe "when converting to yaml" do + before do + @catalog = Puppet::Resource::Catalog.new("me") + @catalog.add_edge("one", "two") + end + + it "should be able to be dumped to yaml" do + YAML.dump(@catalog).should be_instance_of(String) + end + end + + describe "when converting from yaml" do + before do + @catalog = Puppet::Resource::Catalog.new("me") + @catalog.add_edge("one", "two") + + text = YAML.dump(@catalog) + @newcatalog = YAML.load(text) + end + + it "should get converted back to a catalog" do + @newcatalog.should be_instance_of(Puppet::Resource::Catalog) + end + + it "should have all vertices" do + @newcatalog.vertex?("one").should be_true + @newcatalog.vertex?("two").should be_true + end + + it "should have all edges" do + @newcatalog.edge?("one", "two").should be_true + end + end +end + +describe Puppet::Resource::Catalog, "when converting to pson" do + confine "Missing 'pson' library" => Puppet.features.pson? + + before do + @catalog = Puppet::Resource::Catalog.new("myhost") + end + + def pson_output_should + @catalog.class.expects(:pson_create).with { |hash| yield hash }.returns(:something) + end + + # LAK:NOTE For all of these tests, we convert back to the resource so we can + # trap the actual data structure then. + it "should set its document_type to 'Catalog'" do + pson_output_should { |hash| hash['document_type'] == "Catalog" } + + PSON.parse @catalog.to_pson + end + + it "should set its data as a hash" do + pson_output_should { |hash| hash['data'].is_a?(Hash) } + PSON.parse @catalog.to_pson + end + + [:name, :version, :tags, :classes].each do |param| + it "should set its #{param} to the #{param} of the resource" do + @catalog.send(param.to_s + "=", "testing") unless @catalog.send(param) + + pson_output_should { |hash| hash['data'][param.to_s] == @catalog.send(param) } + PSON.parse @catalog.to_pson + end + end + + it "should convert its resources to a PSON-encoded array and store it as the 'resources' data" do + one = stub 'one', :to_pson_data_hash => "one_resource", :ref => "Foo[one]" + two = stub 'two', :to_pson_data_hash => "two_resource", :ref => "Foo[two]" + + @catalog.add_resource(one) + @catalog.add_resource(two) + + # TODO this should really guarantee sort order + PSON.parse(@catalog.to_pson,:create_additions => false)['data']['resources'].sort.should == ["one_resource", "two_resource"].sort + + end + + it "should convert its edges to a PSON-encoded array and store it as the 'edges' data" do + one = stub 'one', :to_pson_data_hash => "one_resource", :ref => 'Foo[one]' + two = stub 'two', :to_pson_data_hash => "two_resource", :ref => 'Foo[two]' + three = stub 'three', :to_pson_data_hash => "three_resource", :ref => 'Foo[three]' + + @catalog.add_edge(one, two) + @catalog.add_edge(two, three) + + @catalog.edge(one, two ).expects(:to_pson_data_hash).returns "one_two_pson" + @catalog.edge(two, three).expects(:to_pson_data_hash).returns "two_three_pson" + + PSON.parse(@catalog.to_pson,:create_additions => false)['data']['edges'].sort.should == %w{one_two_pson two_three_pson}.sort + end +end + +describe Puppet::Resource::Catalog, "when converting from pson" do + confine "Missing 'pson' library" => Puppet.features.pson? + + def pson_result_should + Puppet::Resource::Catalog.expects(:new).with { |hash| yield hash } + end + + before do + @data = { + 'name' => "myhost" + } + @pson = { + 'document_type' => 'Puppet::Resource::Catalog', + 'data' => @data, + 'metadata' => {} + } + + @catalog = Puppet::Resource::Catalog.new("myhost") + Puppet::Resource::Catalog.stubs(:new).returns @catalog + end + + it "should be extended with the PSON utility module" do + Puppet::Resource::Catalog.singleton_class.ancestors.should be_include(Puppet::Util::Pson) + end + + it "should create it with the provided name" do + Puppet::Resource::Catalog.expects(:new).with('myhost').returns @catalog + PSON.parse @pson.to_pson + end + + it "should set the provided version on the catalog if one is set" do + @data['version'] = 50 + PSON.parse @pson.to_pson + @catalog.version.should == @data['version'] + end + + it "should set any provided tags on the catalog" do + @data['tags'] = %w{one two} + PSON.parse @pson.to_pson + @catalog.tags.should == @data['tags'] + end + + it "should set any provided classes on the catalog" do + @data['classes'] = %w{one two} + PSON.parse @pson.to_pson + @catalog.classes.should == @data['classes'] + end + + it 'should convert the resources list into resources and add each of them' do + @data['resources'] = [Puppet::Resource.new(:file, "/foo"), Puppet::Resource.new(:file, "/bar")] + + @catalog.expects(:add_resource).times(2).with { |res| res.type == "File" } + PSON.parse @pson.to_pson + end + + it 'should convert resources even if they do not include "type" information' do + @data['resources'] = [Puppet::Resource.new(:file, "/foo")] + + @data['resources'][0].expects(:to_pson).returns '{"title":"/foo","tags":["file"],"type":"File"}' + + @catalog.expects(:add_resource).with { |res| res.type == "File" } + + PSON.parse @pson.to_pson + end + + it 'should convert the edges list into edges and add each of them' do + one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") + two = Puppet::Relationship.new("tsource", "ttarget", :event => "two", :callback => "refresh") + + @data['edges'] = [one, two] + + @catalog.stubs(:resource).returns("eh") + + @catalog.expects(:add_edge).with { |edge| edge.event == "one" } + @catalog.expects(:add_edge).with { |edge| edge.event == "two" } + + PSON.parse @pson.to_pson + end + + it "should be able to convert relationships that do not include 'type' information" do + one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") + one.expects(:to_pson).returns "{\"event\":\"one\",\"callback\":\"refresh\",\"source\":\"osource\",\"target\":\"otarget\"}" + + @data['edges'] = [one] + + @catalog.stubs(:resource).returns("eh") + + @catalog.expects(:add_edge).with { |edge| edge.event == "one" } + + PSON.parse @pson.to_pson + end + + it "should set the source and target for each edge to the actual resource" do + edge = Puppet::Relationship.new("source", "target") + + @data['edges'] = [edge] + + @catalog.expects(:resource).with("source").returns("source_resource") + @catalog.expects(:resource).with("target").returns("target_resource") + + @catalog.expects(:add_edge).with { |edge| edge.source == "source_resource" and edge.target == "target_resource" } + + PSON.parse @pson.to_pson + end + + it "should fail if the source resource cannot be found" do + edge = Puppet::Relationship.new("source", "target") + + @data['edges'] = [edge] + + @catalog.expects(:resource).with("source").returns(nil) + @catalog.stubs(:resource).with("target").returns("target_resource") + + lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) + end + + it "should fail if the target resource cannot be found" do + edge = Puppet::Relationship.new("source", "target") + + @data['edges'] = [edge] + + @catalog.stubs(:resource).with("source").returns("source_resource") + @catalog.expects(:resource).with("target").returns(nil) + + lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) + end +end diff --git a/spec/unit/resource/catalog_spec_spec.rb b/spec/unit/resource/catalog_spec_spec.rb deleted file mode 100755 index e633b131c..000000000 --- a/spec/unit/resource/catalog_spec_spec.rb +++ /dev/null @@ -1,1067 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Resource::Catalog, "when compiling" do - - before do - @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" - end - - it "should be an Expirer" do - Puppet::Resource::Catalog.ancestors.should be_include(Puppet::Util::Cacher::Expirer) - end - - it "should always be expired if it's not applying" do - @catalog = Puppet::Resource::Catalog.new("host") - @catalog.expects(:applying?).returns false - @catalog.should be_dependent_data_expired(Time.now) - end - - it "should not be expired if it's applying and the timestamp is late enough" do - @catalog = Puppet::Resource::Catalog.new("host") - @catalog.expire - @catalog.expects(:applying?).returns true - @catalog.should_not be_dependent_data_expired(Time.now) - end - - it "should be able to write its list of classes to the class file" do - @catalog = Puppet::Resource::Catalog.new("host") - - @catalog.add_class "foo", "bar" - - Puppet.settings.expects(:value).with(:classfile).returns "/class/file" - - fh = mock 'filehandle' - File.expects(:open).with("/class/file", "w").yields fh - - fh.expects(:puts).with "foo\nbar" - - @catalog.write_class_file - end - - it "should have a client_version attribute" do - @catalog = Puppet::Resource::Catalog.new("host") - @catalog.client_version = 5 - @catalog.client_version.should == 5 - end - - it "should have a server_version attribute" do - @catalog = Puppet::Resource::Catalog.new("host") - @catalog.server_version = 5 - @catalog.server_version.should == 5 - end - - describe "when compiling" do - it "should accept tags" do - config = Puppet::Resource::Catalog.new("mynode") - config.tag("one") - config.tags.should == %w{one} - end - - it "should accept multiple tags at once" do - config = Puppet::Resource::Catalog.new("mynode") - config.tag("one", "two") - config.tags.should == %w{one two} - end - - it "should convert all tags to strings" do - config = Puppet::Resource::Catalog.new("mynode") - config.tag("one", :two) - config.tags.should == %w{one two} - end - - it "should tag with both the qualified name and the split name" do - config = Puppet::Resource::Catalog.new("mynode") - config.tag("one::two") - config.tags.include?("one").should be_true - config.tags.include?("one::two").should be_true - end - - it "should accept classes" do - config = Puppet::Resource::Catalog.new("mynode") - config.add_class("one") - config.classes.should == %w{one} - config.add_class("two", "three") - config.classes.should == %w{one two three} - end - - it "should tag itself with passed class names" do - config = Puppet::Resource::Catalog.new("mynode") - config.add_class("one") - config.tags.should == %w{one} - end - end - - describe "when extracting transobjects" do - - def mkscope - @node = Puppet::Node.new("mynode") - @compiler = Puppet::Parser::Compiler.new(@node) - - # XXX This is ridiculous. - @compiler.send(:evaluate_main) - @scope = @compiler.topscope - end - - def mkresource(type, name) - Puppet::Parser::Resource.new(type, name, :source => @source, :scope => @scope) - end - - it "should fail if no 'main' stage can be found" do - lambda { Puppet::Resource::Catalog.new("mynode").extract }.should raise_error(Puppet::DevError) - end - - it "should warn if any non-main stages are present" do - config = Puppet::Resource::Catalog.new("mynode") - - @scope = mkscope - @source = mock 'source' - - main = mkresource("stage", "main") - config.add_resource(main) - - other = mkresource("stage", "other") - config.add_resource(other) - - Puppet.expects(:warning) - - config.extract - end - - it "should always create a TransBucket for the 'main' stage" do - config = Puppet::Resource::Catalog.new("mynode") - - @scope = mkscope - @source = mock 'source' - - main = mkresource("stage", "main") - config.add_resource(main) - - result = config.extract - result.type.should == "Stage" - result.name.should == "main" - end - - # Now try it with a more complicated graph -- a three tier graph, each tier - it "should transform arbitrarily deep graphs into isomorphic trees" do - config = Puppet::Resource::Catalog.new("mynode") - - @scope = mkscope - @scope.stubs(:tags).returns([]) - @source = mock 'source' - - # Create our scopes. - top = mkresource "stage", "main" - - config.add_resource top - topbucket = [] - topbucket.expects(:classes=).with([]) - top.expects(:to_trans).returns(topbucket) - topres = mkresource "file", "/top" - topres.expects(:to_trans).returns(:topres) - config.add_edge top, topres - - middle = mkresource "class", "middle" - middle.expects(:to_trans).returns([]) - config.add_edge top, middle - midres = mkresource "file", "/mid" - midres.expects(:to_trans).returns(:midres) - config.add_edge middle, midres - - bottom = mkresource "class", "bottom" - bottom.expects(:to_trans).returns([]) - config.add_edge middle, bottom - botres = mkresource "file", "/bot" - botres.expects(:to_trans).returns(:botres) - config.add_edge bottom, botres - - toparray = config.extract - - # This is annoying; it should look like: - # [[[:botres], :midres], :topres] - # but we can't guarantee sort order. - toparray.include?(:topres).should be_true - - midarray = toparray.find { |t| t.is_a?(Array) } - midarray.include?(:midres).should be_true - botarray = midarray.find { |t| t.is_a?(Array) } - botarray.include?(:botres).should be_true - end - end - - describe " when converting to a Puppet::Resource catalog" do - before do - @original = Puppet::Resource::Catalog.new("mynode") - @original.tag(*%w{one two three}) - @original.add_class *%w{four five six} - - @top = Puppet::TransObject.new 'top', "class" - @topobject = Puppet::TransObject.new '/topobject', "file" - @middle = Puppet::TransObject.new 'middle', "class" - @middleobject = Puppet::TransObject.new '/middleobject', "file" - @bottom = Puppet::TransObject.new 'bottom', "class" - @bottomobject = Puppet::TransObject.new '/bottomobject', "file" - - @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] - - @original.add_resource(*@resources) - - @original.add_edge(@top, @topobject) - @original.add_edge(@top, @middle) - @original.add_edge(@middle, @middleobject) - @original.add_edge(@middle, @bottom) - @original.add_edge(@bottom, @bottomobject) - - @catalog = @original.to_resource - end - - it "should copy over the version" do - @original.version = "foo" - @original.to_resource.version.should == "foo" - end - - it "should convert parser resources to plain resources" do - resource = Puppet::Parser::Resource.new(:file, "foo", :scope => stub("scope"), :source => stub("source")) - catalog = Puppet::Resource::Catalog.new("whev") - catalog.add_resource(resource) - new = catalog.to_resource - new.resource(:file, "foo").class.should == Puppet::Resource - end - - it "should add all resources as Puppet::Resource instances" do - @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Resource) } - end - - it "should copy the tag list to the new catalog" do - @catalog.tags.sort.should == @original.tags.sort - end - - it "should copy the class list to the new catalog" do - @catalog.classes.should == @original.classes - end - - it "should duplicate the original edges" do - @original.edges.each do |edge| - @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true - end - end - - it "should set itself as the catalog for each converted resource" do - @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } - end - end - - describe "when converting to a RAL catalog" do - before do - @original = Puppet::Resource::Catalog.new("mynode") - @original.tag(*%w{one two three}) - @original.add_class *%w{four five six} - - @top = Puppet::Resource.new :class, 'top' - @topobject = Puppet::Resource.new :file, @basepath+'/topobject' - @middle = Puppet::Resource.new :class, 'middle' - @middleobject = Puppet::Resource.new :file, @basepath+'/middleobject' - @bottom = Puppet::Resource.new :class, 'bottom' - @bottomobject = Puppet::Resource.new :file, @basepath+'/bottomobject' - - @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] - - @original.add_resource(*@resources) - - @original.add_edge(@top, @topobject) - @original.add_edge(@top, @middle) - @original.add_edge(@middle, @middleobject) - @original.add_edge(@middle, @bottom) - @original.add_edge(@bottom, @bottomobject) - - @catalog = @original.to_ral - end - - it "should add all resources as RAL instances" do - @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Type) } - end - - it "should copy the tag list to the new catalog" do - @catalog.tags.sort.should == @original.tags.sort - end - - it "should copy the class list to the new catalog" do - @catalog.classes.should == @original.classes - end - - it "should duplicate the original edges" do - @original.edges.each do |edge| - @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true - end - end - - it "should set itself as the catalog for each converted resource" do - @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } - end - - # This tests #931. - it "should not lose track of resources whose names vary" do - changer = Puppet::TransObject.new 'changer', 'test' - - config = Puppet::Resource::Catalog.new('test') - config.add_resource(changer) - config.add_resource(@top) - - config.add_edge(@top, changer) - - resource = stub 'resource', :name => "changer2", :title => "changer2", :ref => "Test[changer2]", :catalog= => nil, :remove => nil - - #changer is going to get duplicated as part of a fix for aliases 1094 - changer.expects(:dup).returns(changer) - changer.expects(:to_ral).returns(resource) - - newconfig = nil - - proc { @catalog = config.to_ral }.should_not raise_error - @catalog.resource("Test[changer2]").should equal(resource) - end - - after do - # Remove all resource instances. - @catalog.clear(true) - end - end - - describe "when filtering" do - before :each do - @original = Puppet::Resource::Catalog.new("mynode") - @original.tag(*%w{one two three}) - @original.add_class *%w{four five six} - - @r1 = stub_everything 'r1', :ref => "File[/a]" - @r1.stubs(:respond_to?).with(:ref).returns(true) - @r1.stubs(:dup).returns(@r1) - @r1.stubs(:is_a?).returns(Puppet::Resource).returns(true) - - @r2 = stub_everything 'r2', :ref => "File[/b]" - @r2.stubs(:respond_to?).with(:ref).returns(true) - @r2.stubs(:dup).returns(@r2) - @r2.stubs(:is_a?).returns(Puppet::Resource).returns(true) - - @resources = [@r1,@r2] - - @original.add_resource(@r1,@r2) - end - - it "should transform the catalog to a resource catalog" do - @original.expects(:to_catalog).with { |h,b| h == :to_resource } - - @original.filter - end - - it "should scan each catalog resource in turn and apply filtering block" do - @resources.each { |r| r.expects(:test?) } - @original.filter do |r| - r.test? - end - end - - it "should filter out resources which produce true when the filter block is evaluated" do - @original.filter do |r| - r == @r1 - end.resource("File[/a]").should be_nil - end - - it "should not consider edges against resources that were filtered out" do - @original.add_edge(@r1,@r2) - @original.filter do |r| - r == @r1 - end.edge(@r1,@r2).should be_empty - end - end - - describe "when functioning as a resource container" do - before do - @catalog = Puppet::Resource::Catalog.new("host") - @one = Puppet::Type.type(:notify).new :name => "one" - @two = Puppet::Type.type(:notify).new :name => "two" - @dupe = Puppet::Type.type(:notify).new :name => "one" - end - - it "should provide a method to add one or more resources" do - @catalog.add_resource @one, @two - @catalog.resource(@one.ref).should equal(@one) - @catalog.resource(@two.ref).should equal(@two) - end - - it "should add resources to the relationship graph if it exists" do - relgraph = @catalog.relationship_graph - @catalog.add_resource @one - relgraph.should be_vertex(@one) - end - - it "should yield added resources if a block is provided" do - yielded = [] - @catalog.add_resource(@one, @two) { |r| yielded << r } - yielded.length.should == 2 - end - - it "should set itself as the resource's catalog if it is not a relationship graph" do - @one.expects(:catalog=).with(@catalog) - @catalog.add_resource @one - end - - it "should make all vertices available by resource reference" do - @catalog.add_resource(@one) - @catalog.resource(@one.ref).should equal(@one) - @catalog.vertices.find { |r| r.ref == @one.ref }.should equal(@one) - end - - it "should canonize how resources are referred to during retrieval when both type and title are provided" do - @catalog.add_resource(@one) - - @catalog.resource("notify", "one").should equal(@one) - end - - it "should canonize how resources are referred to during retrieval when just the title is provided" do - @catalog.add_resource(@one) - - @catalog.resource("notify[one]", nil).should equal(@one) - end - - it "should not allow two resources with the same resource reference" do - @catalog.add_resource(@one) - - proc { @catalog.add_resource(@dupe) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) - end - - it "should not store objects that do not respond to :ref" do - proc { @catalog.add_resource("thing") }.should raise_error(ArgumentError) - end - - it "should remove all resources when asked" do - @catalog.add_resource @one - @catalog.add_resource @two - @one.expects :remove - @two.expects :remove - @catalog.clear(true) - end - - it "should support a mechanism for finishing resources" do - @one.expects :finish - @two.expects :finish - @catalog.add_resource @one - @catalog.add_resource @two - - @catalog.finalize - end - - it "should make default resources when finalizing" do - @catalog.expects(:make_default_resources) - @catalog.finalize - end - - it "should add default resources to the catalog upon creation" do - @catalog.make_default_resources - @catalog.resource(:schedule, "daily").should_not be_nil - end - - it "should optionally support an initialization block and should finalize after such blocks" do - @one.expects :finish - @two.expects :finish - config = Puppet::Resource::Catalog.new("host") do |conf| - conf.add_resource @one - conf.add_resource @two - end - end - - it "should inform the resource that it is the resource's catalog" do - @one.expects(:catalog=).with(@catalog) - @catalog.add_resource @one - end - - it "should be able to find resources by reference" do - @catalog.add_resource @one - @catalog.resource(@one.ref).should equal(@one) - end - - it "should be able to find resources by reference or by type/title tuple" do - @catalog.add_resource @one - @catalog.resource("notify", "one").should equal(@one) - end - - it "should have a mechanism for removing resources" do - @catalog.add_resource @one - @one.expects :remove - @catalog.remove_resource(@one) - @catalog.resource(@one.ref).should be_nil - @catalog.vertex?(@one).should be_false - end - - it "should have a method for creating aliases for resources" do - @catalog.add_resource @one - @catalog.alias(@one, "other") - @catalog.resource("notify", "other").should equal(@one) - end - - it "should ignore conflicting aliases that point to the aliased resource" do - @catalog.alias(@one, "other") - lambda { @catalog.alias(@one, "other") }.should_not raise_error - end - - it "should create aliases for resources isomorphic resources whose names do not match their titles" do - resource = Puppet::Type::File.new(:title => "testing", :path => @basepath+"/something") - - @catalog.add_resource(resource) - - @catalog.resource(:file, @basepath+"/something").should equal(resource) - end - - it "should not create aliases for resources non-isomorphic resources whose names do not match their titles" do - resource = Puppet::Type.type(:exec).new(:title => "testing", :command => "echo", :path => %w{/bin /usr/bin /usr/local/bin}) - - @catalog.add_resource(resource) - - # Yay, I've already got a 'should' method - @catalog.resource(:exec, "echo").object_id.should == nil.object_id - end - - # This test is the same as the previous, but the behaviour should be explicit. - it "should alias using the class name from the resource reference, not the resource class name" do - @catalog.add_resource @one - @catalog.alias(@one, "other") - @catalog.resource("notify", "other").should equal(@one) - end - - it "should ignore conflicting aliases that point to the aliased resource" do - @catalog.alias(@one, "other") - lambda { @catalog.alias(@one, "other") }.should_not raise_error - end - - it "should fail to add an alias if the aliased name already exists" do - @catalog.add_resource @one - proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError) - end - - it "should not fail when a resource has duplicate aliases created" do - @catalog.add_resource @one - proc { @catalog.alias @one, "one" }.should_not raise_error - end - - it "should not create aliases that point back to the resource" do - @catalog.alias(@one, "one") - @catalog.resource(:notify, "one").should be_nil - end - - it "should be able to look resources up by their aliases" do - @catalog.add_resource @one - @catalog.alias @one, "two" - @catalog.resource(:notify, "two").should equal(@one) - end - - it "should remove resource aliases when the target resource is removed" do - @catalog.add_resource @one - @catalog.alias(@one, "other") - @one.expects :remove - @catalog.remove_resource(@one) - @catalog.resource("notify", "other").should be_nil - end - - it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do - resource = Puppet::Type.type(:file).new :path => @basepath+"/something", :title => "other", :content => "blah" - resource.expects(:isomorphic?).returns(true) - @catalog.add_resource(resource) - @catalog.resource(:file, "other").should equal(resource) - @catalog.resource(:file, @basepath+"/something").ref.should == resource.ref - end - - it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do - resource = Puppet::Type.type(:file).new :path => @basepath+"/something", :title => "other", :content => "blah" - resource.expects(:isomorphic?).returns(false) - @catalog.add_resource(resource) - @catalog.resource(:file, resource.title).should equal(resource) - # We can't use .should here, because the resources respond to that method. - if @catalog.resource(:file, resource.name) - raise "Aliased non-isomorphic resource" - end - end - - it "should provide a method to create additional resources that also registers the resource" do - args = {:name => "/yay", :ensure => :file} - resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" - Puppet::Type.type(:file).expects(:new).with(args).returns(resource) - @catalog.create_resource :file, args - @catalog.resource("File[/yay]").should equal(resource) - end - end - - describe "when applying" do - before :each do - @catalog = Puppet::Resource::Catalog.new("host") - - @transaction = mock 'transaction' - Puppet::Transaction.stubs(:new).returns(@transaction) - @transaction.stubs(:evaluate) - @transaction.stubs(:add_times) - end - - it "should create and evaluate a transaction" do - @transaction.expects(:evaluate) - @catalog.apply - end - - it "should provide the catalog retrieval time to the transaction" do - @catalog.retrieval_duration = 5 - @transaction.expects(:add_times).with(:config_retrieval => 5) - @catalog.apply - end - - it "should use a retrieval time of 0 if none is set in the catalog" do - @catalog.retrieval_duration = nil - @transaction.expects(:add_times).with(:config_retrieval => 0) - @catalog.apply - end - - it "should return the transaction" do - @catalog.apply.should equal(@transaction) - end - - it "should yield the transaction if a block is provided" do - @catalog.apply do |trans| - trans.should equal(@transaction) - end - end - - it "should default to being a host catalog" do - @catalog.host_config.should be_true - end - - it "should be able to be set to a non-host_config" do - @catalog.host_config = false - @catalog.host_config.should be_false - end - - it "should pass supplied tags on to the transaction" do - @transaction.expects(:tags=).with(%w{one two}) - @catalog.apply(:tags => %w{one two}) - end - - it "should set ignoreschedules on the transaction if specified in apply()" do - @transaction.expects(:ignoreschedules=).with(true) - @catalog.apply(:ignoreschedules => true) - end - - it "should expire cached data in the resources both before and after the transaction" do - @catalog.expects(:expire).times(2) - @catalog.apply - end - - describe "host catalogs" do - - # super() doesn't work in the setup method for some reason - before do - @catalog.host_config = true - Puppet::Util::Storage.stubs(:store) - end - - it "should initialize the state database before applying a catalog" do - Puppet::Util::Storage.expects(:load) - - # Short-circuit the apply, so we know we're loading before the transaction - Puppet::Transaction.expects(:new).raises ArgumentError - proc { @catalog.apply }.should raise_error(ArgumentError) - end - - it "should sync the state database after applying" do - Puppet::Util::Storage.expects(:store) - @transaction.stubs :any_failed? => false - @catalog.apply - end - - after { Puppet.settings.clear } - end - - describe "non-host catalogs" do - - before do - @catalog.host_config = false - end - - it "should never send reports" do - Puppet[:report] = true - Puppet[:summarize] = true - @catalog.apply - end - - it "should never modify the state database" do - Puppet::Util::Storage.expects(:load).never - Puppet::Util::Storage.expects(:store).never - @catalog.apply - end - - after { Puppet.settings.clear } - end - end - - describe "when creating a relationship graph" do - before do - Puppet::Type.type(:component) - @catalog = Puppet::Resource::Catalog.new("host") - @compone = Puppet::Type::Component.new :name => "one" - @comptwo = Puppet::Type::Component.new :name => "two", :require => "Class[one]" - @file = Puppet::Type.type(:file) - @one = @file.new :path => @basepath+"/one" - @two = @file.new :path => @basepath+"/two" - @sub = @file.new :path => @basepath+"/two/subdir" - @catalog.add_edge @compone, @one - @catalog.add_edge @comptwo, @two - - @three = @file.new :path => @basepath+"/three" - @four = @file.new :path => @basepath+"/four", :require => "File[#{@basepath}/three]" - @five = @file.new :path => @basepath+"/five" - @catalog.add_resource @compone, @comptwo, @one, @two, @three, @four, @five, @sub - - @relationships = @catalog.relationship_graph - end - - it "should be able to create a relationship graph" do - @relationships.should be_instance_of(Puppet::SimpleGraph) - end - - it "should not have any components" do - @relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil - end - - it "should have all non-component resources from the catalog" do - # The failures print out too much info, so i just do a class comparison - @relationships.vertex?(@five).should be_true - end - - it "should have all resource relationships set as edges" do - @relationships.edge?(@three, @four).should be_true - end - - it "should copy component relationships to all contained resources" do - @relationships.edge?(@one, @two).should be_true - end - - it "should add automatic relationships to the relationship graph" do - @relationships.edge?(@two, @sub).should be_true - end - - it "should get removed when the catalog is cleaned up" do - @relationships.expects(:clear) - @catalog.clear - @catalog.instance_variable_get("@relationship_graph").should be_nil - end - - it "should write :relationships and :expanded_relationships graph files if the catalog is a host catalog" do - @catalog.clear - graph = Puppet::SimpleGraph.new - Puppet::SimpleGraph.expects(:new).returns graph - - graph.expects(:write_graph).with(:relationships) - graph.expects(:write_graph).with(:expanded_relationships) - - @catalog.host_config = true - - @catalog.relationship_graph - end - - it "should not write graph files if the catalog is not a host catalog" do - @catalog.clear - graph = Puppet::SimpleGraph.new - Puppet::SimpleGraph.expects(:new).returns graph - - graph.expects(:write_graph).never - - @catalog.host_config = false - - @catalog.relationship_graph - end - - it "should create a new relationship graph after clearing the old one" do - @relationships.expects(:clear) - @catalog.clear - @catalog.relationship_graph.should be_instance_of(Puppet::SimpleGraph) - end - - it "should remove removed resources from the relationship graph if it exists" do - @catalog.remove_resource(@one) - @catalog.relationship_graph.vertex?(@one).should be_false - end - end - - describe "when writing dot files" do - before do - @catalog = Puppet::Resource::Catalog.new("host") - @name = :test - @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") - end - - it "should only write when it is a host catalog" do - File.expects(:open).with(@file).never - @catalog.host_config = false - Puppet[:graph] = true - @catalog.write_graph(@name) - end - - after do - Puppet.settings.clear - end - end - - describe "when indirecting" do - before do - @real_indirection = Puppet::Resource::Catalog.indirection - - @indirection = stub 'indirection', :name => :catalog - - Puppet::Util::Cacher.expire - end - - it "should redirect to the indirection for retrieval" do - Puppet::Resource::Catalog.stubs(:indirection).returns(@indirection) - @indirection.expects(:find) - Puppet::Resource::Catalog.find(:myconfig) - end - - it "should use the value of the 'catalog_terminus' setting to determine its terminus class" do - # Puppet only checks the terminus setting the first time you ask - # so this returns the object to the clean state - # at the expense of making this test less pure - Puppet::Resource::Catalog.indirection.reset_terminus_class - - Puppet.settings[:catalog_terminus] = "rest" - Puppet::Resource::Catalog.indirection.terminus_class.should == :rest - end - - it "should allow the terminus class to be set manually" do - Puppet::Resource::Catalog.indirection.terminus_class = :rest - Puppet::Resource::Catalog.indirection.terminus_class.should == :rest - end - - after do - Puppet::Util::Cacher.expire - @real_indirection.reset_terminus_class - end - end - - describe "when converting to yaml" do - before do - @catalog = Puppet::Resource::Catalog.new("me") - @catalog.add_edge("one", "two") - end - - it "should be able to be dumped to yaml" do - YAML.dump(@catalog).should be_instance_of(String) - end - end - - describe "when converting from yaml" do - before do - @catalog = Puppet::Resource::Catalog.new("me") - @catalog.add_edge("one", "two") - - text = YAML.dump(@catalog) - @newcatalog = YAML.load(text) - end - - it "should get converted back to a catalog" do - @newcatalog.should be_instance_of(Puppet::Resource::Catalog) - end - - it "should have all vertices" do - @newcatalog.vertex?("one").should be_true - @newcatalog.vertex?("two").should be_true - end - - it "should have all edges" do - @newcatalog.edge?("one", "two").should be_true - end - end -end - -describe Puppet::Resource::Catalog, "when converting to pson" do - confine "Missing 'pson' library" => Puppet.features.pson? - - before do - @catalog = Puppet::Resource::Catalog.new("myhost") - end - - def pson_output_should - @catalog.class.expects(:pson_create).with { |hash| yield hash }.returns(:something) - end - - # LAK:NOTE For all of these tests, we convert back to the resource so we can - # trap the actual data structure then. - it "should set its document_type to 'Catalog'" do - pson_output_should { |hash| hash['document_type'] == "Catalog" } - - PSON.parse @catalog.to_pson - end - - it "should set its data as a hash" do - pson_output_should { |hash| hash['data'].is_a?(Hash) } - PSON.parse @catalog.to_pson - end - - [:name, :version, :tags, :classes].each do |param| - it "should set its #{param} to the #{param} of the resource" do - @catalog.send(param.to_s + "=", "testing") unless @catalog.send(param) - - pson_output_should { |hash| hash['data'][param.to_s] == @catalog.send(param) } - PSON.parse @catalog.to_pson - end - end - - it "should convert its resources to a PSON-encoded array and store it as the 'resources' data" do - one = stub 'one', :to_pson_data_hash => "one_resource", :ref => "Foo[one]" - two = stub 'two', :to_pson_data_hash => "two_resource", :ref => "Foo[two]" - - @catalog.add_resource(one) - @catalog.add_resource(two) - - # TODO this should really guarantee sort order - PSON.parse(@catalog.to_pson,:create_additions => false)['data']['resources'].sort.should == ["one_resource", "two_resource"].sort - - end - - it "should convert its edges to a PSON-encoded array and store it as the 'edges' data" do - one = stub 'one', :to_pson_data_hash => "one_resource", :ref => 'Foo[one]' - two = stub 'two', :to_pson_data_hash => "two_resource", :ref => 'Foo[two]' - three = stub 'three', :to_pson_data_hash => "three_resource", :ref => 'Foo[three]' - - @catalog.add_edge(one, two) - @catalog.add_edge(two, three) - - @catalog.edge(one, two ).expects(:to_pson_data_hash).returns "one_two_pson" - @catalog.edge(two, three).expects(:to_pson_data_hash).returns "two_three_pson" - - PSON.parse(@catalog.to_pson,:create_additions => false)['data']['edges'].sort.should == %w{one_two_pson two_three_pson}.sort - end -end - -describe Puppet::Resource::Catalog, "when converting from pson" do - confine "Missing 'pson' library" => Puppet.features.pson? - - def pson_result_should - Puppet::Resource::Catalog.expects(:new).with { |hash| yield hash } - end - - before do - @data = { - 'name' => "myhost" - } - @pson = { - 'document_type' => 'Puppet::Resource::Catalog', - 'data' => @data, - 'metadata' => {} - } - - @catalog = Puppet::Resource::Catalog.new("myhost") - Puppet::Resource::Catalog.stubs(:new).returns @catalog - end - - it "should be extended with the PSON utility module" do - Puppet::Resource::Catalog.singleton_class.ancestors.should be_include(Puppet::Util::Pson) - end - - it "should create it with the provided name" do - Puppet::Resource::Catalog.expects(:new).with('myhost').returns @catalog - PSON.parse @pson.to_pson - end - - it "should set the provided version on the catalog if one is set" do - @data['version'] = 50 - PSON.parse @pson.to_pson - @catalog.version.should == @data['version'] - end - - it "should set any provided tags on the catalog" do - @data['tags'] = %w{one two} - PSON.parse @pson.to_pson - @catalog.tags.should == @data['tags'] - end - - it "should set any provided classes on the catalog" do - @data['classes'] = %w{one two} - PSON.parse @pson.to_pson - @catalog.classes.should == @data['classes'] - end - - it 'should convert the resources list into resources and add each of them' do - @data['resources'] = [Puppet::Resource.new(:file, "/foo"), Puppet::Resource.new(:file, "/bar")] - - @catalog.expects(:add_resource).times(2).with { |res| res.type == "File" } - PSON.parse @pson.to_pson - end - - it 'should convert resources even if they do not include "type" information' do - @data['resources'] = [Puppet::Resource.new(:file, "/foo")] - - @data['resources'][0].expects(:to_pson).returns '{"title":"/foo","tags":["file"],"type":"File"}' - - @catalog.expects(:add_resource).with { |res| res.type == "File" } - - PSON.parse @pson.to_pson - end - - it 'should convert the edges list into edges and add each of them' do - one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") - two = Puppet::Relationship.new("tsource", "ttarget", :event => "two", :callback => "refresh") - - @data['edges'] = [one, two] - - @catalog.stubs(:resource).returns("eh") - - @catalog.expects(:add_edge).with { |edge| edge.event == "one" } - @catalog.expects(:add_edge).with { |edge| edge.event == "two" } - - PSON.parse @pson.to_pson - end - - it "should be able to convert relationships that do not include 'type' information" do - one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") - one.expects(:to_pson).returns "{\"event\":\"one\",\"callback\":\"refresh\",\"source\":\"osource\",\"target\":\"otarget\"}" - - @data['edges'] = [one] - - @catalog.stubs(:resource).returns("eh") - - @catalog.expects(:add_edge).with { |edge| edge.event == "one" } - - PSON.parse @pson.to_pson - end - - it "should set the source and target for each edge to the actual resource" do - edge = Puppet::Relationship.new("source", "target") - - @data['edges'] = [edge] - - @catalog.expects(:resource).with("source").returns("source_resource") - @catalog.expects(:resource).with("target").returns("target_resource") - - @catalog.expects(:add_edge).with { |edge| edge.source == "source_resource" and edge.target == "target_resource" } - - PSON.parse @pson.to_pson - end - - it "should fail if the source resource cannot be found" do - edge = Puppet::Relationship.new("source", "target") - - @data['edges'] = [edge] - - @catalog.expects(:resource).with("source").returns(nil) - @catalog.stubs(:resource).with("target").returns("target_resource") - - lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) - end - - it "should fail if the target resource cannot be found" do - edge = Puppet::Relationship.new("source", "target") - - @data['edges'] = [edge] - - @catalog.stubs(:resource).with("source").returns("source_resource") - @catalog.expects(:resource).with("target").returns(nil) - - lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) - end -end diff --git a/spec/unit/resource/status_spec.rb b/spec/unit/resource/status_spec.rb new file mode 100755 index 000000000..b03f9d440 --- /dev/null +++ b/spec/unit/resource/status_spec.rb @@ -0,0 +1,103 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/resource/status' + +describe Puppet::Resource::Status do + before do + @resource = Puppet::Type.type(:file).new :path => "/my/file" + @status = Puppet::Resource::Status.new(@resource) + end + + [:node, :version, :file, :line, :current_values, :skipped_reason, :status, :evaluation_time, :change_count].each do |attr| + it "should support #{attr}" do + @status.send(attr.to_s + "=", "foo") + @status.send(attr).should == "foo" + end + end + + [:skipped, :failed, :restarted, :failed_to_restart, :changed, :out_of_sync, :scheduled].each do |attr| + it "should support #{attr}" do + @status.send(attr.to_s + "=", "foo") + @status.send(attr).should == "foo" + end + + it "should have a boolean method for determining whehter it was #{attr}" do + @status.send(attr.to_s + "=", "foo") + @status.should send("be_#{attr}") + end + end + + it "should accept a resource at initialization" do + Puppet::Resource::Status.new(@resource).resource.should_not be_nil + end + + it "should set its source description to the resource's path" do + @resource.expects(:path).returns "/my/path" + Puppet::Resource::Status.new(@resource).source_description.should == "/my/path" + end + + [:file, :line, :version].each do |attr| + it "should copy the resource's #{attr}" do + @resource.expects(attr).returns "foo" + Puppet::Resource::Status.new(@resource).send(attr).should == "foo" + end + end + + it "should copy the resource's tags" do + @resource.expects(:tags).returns %w{foo bar} + Puppet::Resource::Status.new(@resource).tags.should == %w{foo bar} + end + + it "should always convert the resource to a string" do + @resource.expects(:to_s).returns "foo" + Puppet::Resource::Status.new(@resource).resource.should == "foo" + end + + it "should support tags" do + Puppet::Resource::Status.ancestors.should include(Puppet::Util::Tagging) + end + + it "should create a timestamp at its creation time" do + @status.time.should be_instance_of(Time) + end + + describe "when sending logs" do + before do + Puppet::Util::Log.stubs(:new) + end + + it "should set the tags to the event tags" do + Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} } + @status.stubs(:tags).returns %w{one two} + @status.send_log :notice, "my message" + end + + [:file, :line, :version].each do |attr| + it "should pass the #{attr}" do + Puppet::Util::Log.expects(:new).with { |args| args[attr] == "my val" } + @status.send(attr.to_s + "=", "my val") + @status.send_log :notice, "my message" + end + end + + it "should use the source description as the source" do + Puppet::Util::Log.expects(:new).with { |args| args[:source] == "my source" } + @status.stubs(:source_description).returns "my source" + @status.send_log :notice, "my message" + end + end + + it "should support adding events" do + event = Puppet::Transaction::Event.new(:name => :foobar) + @status.add_event(event) + @status.events.should == [event] + end + + it "should use '<<' to add events" do + event = Puppet::Transaction::Event.new(:name => :foobar) + (@status << event).should equal(@status) + @status.events.should == [event] + end +end diff --git a/spec/unit/resource/status_spec_spec.rb b/spec/unit/resource/status_spec_spec.rb deleted file mode 100755 index b03f9d440..000000000 --- a/spec/unit/resource/status_spec_spec.rb +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/resource/status' - -describe Puppet::Resource::Status do - before do - @resource = Puppet::Type.type(:file).new :path => "/my/file" - @status = Puppet::Resource::Status.new(@resource) - end - - [:node, :version, :file, :line, :current_values, :skipped_reason, :status, :evaluation_time, :change_count].each do |attr| - it "should support #{attr}" do - @status.send(attr.to_s + "=", "foo") - @status.send(attr).should == "foo" - end - end - - [:skipped, :failed, :restarted, :failed_to_restart, :changed, :out_of_sync, :scheduled].each do |attr| - it "should support #{attr}" do - @status.send(attr.to_s + "=", "foo") - @status.send(attr).should == "foo" - end - - it "should have a boolean method for determining whehter it was #{attr}" do - @status.send(attr.to_s + "=", "foo") - @status.should send("be_#{attr}") - end - end - - it "should accept a resource at initialization" do - Puppet::Resource::Status.new(@resource).resource.should_not be_nil - end - - it "should set its source description to the resource's path" do - @resource.expects(:path).returns "/my/path" - Puppet::Resource::Status.new(@resource).source_description.should == "/my/path" - end - - [:file, :line, :version].each do |attr| - it "should copy the resource's #{attr}" do - @resource.expects(attr).returns "foo" - Puppet::Resource::Status.new(@resource).send(attr).should == "foo" - end - end - - it "should copy the resource's tags" do - @resource.expects(:tags).returns %w{foo bar} - Puppet::Resource::Status.new(@resource).tags.should == %w{foo bar} - end - - it "should always convert the resource to a string" do - @resource.expects(:to_s).returns "foo" - Puppet::Resource::Status.new(@resource).resource.should == "foo" - end - - it "should support tags" do - Puppet::Resource::Status.ancestors.should include(Puppet::Util::Tagging) - end - - it "should create a timestamp at its creation time" do - @status.time.should be_instance_of(Time) - end - - describe "when sending logs" do - before do - Puppet::Util::Log.stubs(:new) - end - - it "should set the tags to the event tags" do - Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} } - @status.stubs(:tags).returns %w{one two} - @status.send_log :notice, "my message" - end - - [:file, :line, :version].each do |attr| - it "should pass the #{attr}" do - Puppet::Util::Log.expects(:new).with { |args| args[attr] == "my val" } - @status.send(attr.to_s + "=", "my val") - @status.send_log :notice, "my message" - end - end - - it "should use the source description as the source" do - Puppet::Util::Log.expects(:new).with { |args| args[:source] == "my source" } - @status.stubs(:source_description).returns "my source" - @status.send_log :notice, "my message" - end - end - - it "should support adding events" do - event = Puppet::Transaction::Event.new(:name => :foobar) - @status.add_event(event) - @status.events.should == [event] - end - - it "should use '<<' to add events" do - event = Puppet::Transaction::Event.new(:name => :foobar) - (@status << event).should equal(@status) - @status.events.should == [event] - end -end diff --git a/spec/unit/resource/type_collection_helper_spec.rb b/spec/unit/resource/type_collection_helper_spec.rb new file mode 100644 index 000000000..e390ff952 --- /dev/null +++ b/spec/unit/resource/type_collection_helper_spec.rb @@ -0,0 +1,25 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/resource/type_collection_helper' + +class RTCHelperTester + include Puppet::Resource::TypeCollectionHelper +end + +describe Puppet::Resource::TypeCollectionHelper do + before do + @helper = RTCHelperTester.new + end + + it "should use its current environment to retrieve the known resource type collection" do + env = stub 'environment' + @helper.expects(:environment).returns env + + rtc = stub 'known_resource_types' + env.expects(:known_resource_types).returns rtc + + @helper.known_resource_types.should equal(rtc) + end +end diff --git a/spec/unit/resource/type_collection_helper_spec_spec.rb b/spec/unit/resource/type_collection_helper_spec_spec.rb deleted file mode 100644 index e390ff952..000000000 --- a/spec/unit/resource/type_collection_helper_spec_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/resource/type_collection_helper' - -class RTCHelperTester - include Puppet::Resource::TypeCollectionHelper -end - -describe Puppet::Resource::TypeCollectionHelper do - before do - @helper = RTCHelperTester.new - end - - it "should use its current environment to retrieve the known resource type collection" do - env = stub 'environment' - @helper.expects(:environment).returns env - - rtc = stub 'known_resource_types' - env.expects(:known_resource_types).returns rtc - - @helper.known_resource_types.should equal(rtc) - end -end diff --git a/spec/unit/resource/type_collection_spec.rb b/spec/unit/resource/type_collection_spec.rb new file mode 100644 index 000000000..c0711bb87 --- /dev/null +++ b/spec/unit/resource/type_collection_spec.rb @@ -0,0 +1,432 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/resource/type_collection' +require 'puppet/resource/type' + +describe Puppet::Resource::TypeCollection do + before do + @instance = Puppet::Resource::Type.new(:hostclass, "foo") + @code = Puppet::Resource::TypeCollection.new("env") + end + + it "should require an environment at initialization" do + env = Puppet::Node::Environment.new("testing") + Puppet::Resource::TypeCollection.new(env).environment.should equal(env) + end + + it "should convert the environment into an environment instance if a string is provided" do + env = Puppet::Node::Environment.new("testing") + Puppet::Resource::TypeCollection.new("testing").environment.should equal(env) + end + + it "should create a 'loader' at initialization" do + Puppet::Resource::TypeCollection.new("testing").loader.should be_instance_of(Puppet::Parser::TypeLoader) + end + + it "should be able to add a resource type" do + Puppet::Resource::TypeCollection.new("env").should respond_to(:add) + end + + it "should consider '<<' to be an alias to 'add' but should return self" do + loader = Puppet::Resource::TypeCollection.new("env") + loader.expects(:add).with "foo" + loader.expects(:add).with "bar" + loader << "foo" << "bar" + end + + it "should set itself as the code collection for added resource types" do + loader = Puppet::Resource::TypeCollection.new("env") + + node = Puppet::Resource::Type.new(:node, "foo") + + @code.add(node) + @code.node("foo").should equal(node) + + node.resource_type_collection.should equal(@code) + end + + it "should store node resource types as nodes" do + node = Puppet::Resource::Type.new(:node, "foo") + + @code.add(node) + @code.node("foo").should equal(node) + end + + it "should store hostclasses as hostclasses" do + klass = Puppet::Resource::Type.new(:hostclass, "foo") + + @code.add(klass) + @code.hostclass("foo").should equal(klass) + end + + it "should store definitions as definitions" do + define = Puppet::Resource::Type.new(:definition, "foo") + + @code.add(define) + @code.definition("foo").should equal(define) + end + + it "should merge new classes with existing classes of the same name" do + loader = Puppet::Resource::TypeCollection.new("env") + first = Puppet::Resource::Type.new(:hostclass, "foo") + second = Puppet::Resource::Type.new(:hostclass, "foo") + loader.add first + first.expects(:merge).with(second) + loader.add(second) + end + + it "should remove all nodes, classes, and definitions when cleared" do + loader = Puppet::Resource::TypeCollection.new("env") + loader.add Puppet::Resource::Type.new(:hostclass, "class") + loader.add Puppet::Resource::Type.new(:definition, "define") + loader.add Puppet::Resource::Type.new(:node, "node") + + loader.clear + loader.hostclass("class").should be_nil + loader.definition("define").should be_nil + loader.node("node").should be_nil + end + + describe "when looking up names" do + before do + @type = Puppet::Resource::Type.new(:hostclass, "ns::klass") + end + + it "should support looking up with multiple namespaces" do + @code.add @type + @code.find_hostclass(%w{boo baz ns}, "klass").should equal(@type) + end + + it "should not attempt to import anything when the type is already defined" do + @code.add @type + @code.loader.expects(:import).never + @code.find_hostclass(%w{ns}, "klass").should equal(@type) + end + + describe "that need to be loaded" do + it "should use the loader to load the files" do + @code.loader.expects(:load_until).with(["ns"], "klass") + @code.find_or_load(["ns"], "klass", :hostclass) + end + + it "should downcase the name and downcase and array-fy the namespaces before passing to the loader" do + @code.loader.expects(:load_until).with(["ns"], "klass") + @code.find_or_load("Ns", "Klass", :hostclass) + end + + it "should attempt to find the type when the loader yields" do + @code.loader.expects(:load_until).yields + @code.expects(:find).with(["ns"], "klass", :hostclass).times(2).returns(false).then.returns(true) + @code.find_or_load("ns", "klass", :hostclass) + end + + it "should return the result of 'load_until'" do + @code.loader.expects(:load_until).returns "foo" + @code.find_or_load("Ns", "Klass", :hostclass).should == "foo" + end + + it "should return nil if the name isn't found" do + @code.stubs(:load_until).returns(nil) + @code.find_or_load("Ns", "Klass", :hostclass).should be_nil + end + end + end + + %w{hostclass node definition}.each do |data| + before do + @instance = Puppet::Resource::Type.new(data, "foo") + end + + it "should have a method for adding a #{data}" do + Puppet::Resource::TypeCollection.new("env").should respond_to("add_" + data) + end + + it "should use the name of the instance to add it" do + loader = Puppet::Resource::TypeCollection.new("env") + loader.send("add_#{data}", @instance) + loader.send(data, @instance.name).should equal(@instance) + end + + unless data == "hostclass" + it "should fail to add a #{data} when one already exists" do + loader = Puppet::Resource::TypeCollection.new("env") + loader.add @instance + lambda { loader.add(@instance) }.should raise_error(Puppet::ParseError) + end + end + + it "should return the added #{data}" do + loader = Puppet::Resource::TypeCollection.new("env") + + loader.add(@instance).should equal(@instance) + end + + it "should be able to retrieve #{data} by name" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(data, "bar") + loader.add instance + loader.send(data, "bar").should equal(instance) + end + + it "should retrieve #{data} insensitive to case" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(data, "Bar") + loader.add instance + loader.send(data, "bAr").should equal(instance) + end + + it "should return nil when asked for a #{data} that has not been added" do + Puppet::Resource::TypeCollection.new("env").send(data, "foo").should be_nil + end + + it "should be able to retrieve all #{data}s" do + plurals = { "hostclass" => "hostclasses", "node" => "nodes", "definition" => "definitions" } + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(data, "foo") + loader.add instance + loader.send(plurals[data]).should == { "foo" => instance } + end + end + + describe "when finding a qualified instance" do + it "should return any found instance if the instance name is fully qualified" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") + loader.add instance + loader.find("namespace", "::foo::bar", :hostclass).should equal(instance) + end + + it "should return nil if the instance name is fully qualified and no such instance exists" do + loader = Puppet::Resource::TypeCollection.new("env") + loader.find("namespace", "::foo::bar", :hostclass).should be_nil + end + + it "should be able to find classes in the base namespace" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo") + loader.add instance + loader.find("", "foo", :hostclass).should equal(instance) + end + + it "should return the partially qualified object if it exists in a provided namespace" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") + loader.add instance + loader.find("foo", "bar::baz", :hostclass).should equal(instance) + end + + it "should be able to find partially qualified objects in any of the provided namespaces" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") + loader.add instance + loader.find(["nons", "foo", "otherns"], "bar::baz", :hostclass).should equal(instance) + end + + it "should return the unqualified object if it exists in a provided namespace" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") + loader.add instance + loader.find("foo", "bar", :hostclass).should equal(instance) + end + + it "should return the unqualified object if it exists in the parent namespace" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") + loader.add instance + loader.find("foo::bar::baz", "bar", :hostclass).should equal(instance) + end + + it "should should return the partially qualified object if it exists in the parent namespace" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") + loader.add instance + loader.find("foo::bar", "bar::baz", :hostclass).should equal(instance) + end + + it "should return the qualified object if it exists in the root namespace" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") + loader.add instance + loader.find("foo::bar", "foo::bar::baz", :hostclass).should equal(instance) + end + + it "should return nil if the object cannot be found" do + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") + loader.add instance + loader.find("foo::bar", "eh", :hostclass).should be_nil + end + end + + it "should use the generic 'find' method with an empty namespace to find nodes" do + loader = Puppet::Resource::TypeCollection.new("env") + loader.expects(:find).with("", "bar", :node) + loader.find_node("bar") + end + + it "should use the 'find_or_load' method to find hostclasses" do + loader = Puppet::Resource::TypeCollection.new("env") + loader.expects(:find_or_load).with("foo", "bar", :hostclass) + loader.find_hostclass("foo", "bar") + end + + it "should use the 'find_or_load' method to find definitions" do + loader = Puppet::Resource::TypeCollection.new("env") + loader.expects(:find_or_load).with("foo", "bar", :definition) + loader.find_definition("foo", "bar") + end + + it "should indicate whether any nodes are defined" do + loader = Puppet::Resource::TypeCollection.new("env") + loader.add_node(Puppet::Resource::Type.new(:node, "foo")) + loader.should be_nodes + end + + it "should indicate whether no nodes are defined" do + Puppet::Resource::TypeCollection.new("env").should_not be_nodes + end + + describe "when finding nodes" do + before :each do + @loader = Puppet::Resource::TypeCollection.new("env") + end + + it "should return any node whose name exactly matches the provided node name" do + node = Puppet::Resource::Type.new(:node, "foo") + @loader << node + + @loader.node("foo").should equal(node) + end + + it "should return the first regex node whose regex matches the provided node name" do + node1 = Puppet::Resource::Type.new(:node, /\w/) + node2 = Puppet::Resource::Type.new(:node, /\d/) + @loader << node1 << node2 + + @loader.node("foo10").should equal(node1) + end + + it "should preferentially return a node whose name is string-equal over returning a node whose regex matches a provided name" do + node1 = Puppet::Resource::Type.new(:node, /\w/) + node2 = Puppet::Resource::Type.new(:node, "foo") + @loader << node1 << node2 + + @loader.node("foo").should equal(node2) + end + end + + describe "when managing files" do + before do + @loader = Puppet::Resource::TypeCollection.new("env") + Puppet::Util::LoadedFile.stubs(:new).returns stub("watched_file") + end + + it "should have a method for specifying a file should be watched" do + @loader.should respond_to(:watch_file) + end + + it "should have a method for determining if a file is being watched" do + @loader.watch_file("/foo/bar") + @loader.should be_watching_file("/foo/bar") + end + + it "should use LoadedFile to watch files" do + Puppet::Util::LoadedFile.expects(:new).with("/foo/bar").returns stub("watched_file") + @loader.watch_file("/foo/bar") + end + + it "should be considered stale if any files have changed" do + file1 = stub 'file1', :changed? => false + file2 = stub 'file2', :changed? => true + Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) + @loader.watch_file("/foo/bar") + @loader.watch_file("/other/bar") + + @loader.should be_stale + end + + it "should not be considered stable if no files have changed" do + file1 = stub 'file1', :changed? => false + file2 = stub 'file2', :changed? => false + Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) + @loader.watch_file("/foo/bar") + @loader.watch_file("/other/bar") + + @loader.should_not be_stale + end + end + + describe "when performing initial import" do + before do + @parser = stub 'parser', :file= => nil, :string => nil, :parse => nil + Puppet::Parser::Parser.stubs(:new).returns @parser + @code = Puppet::Resource::TypeCollection.new("env") + end + + it "should create a new parser instance" do + Puppet::Parser::Parser.expects(:new).returns @parser + @code.perform_initial_import + end + + it "should set the parser's string to the 'code' setting and parse if code is available" do + Puppet.settings[:code] = "my code" + @parser.expects(:string=).with "my code" + @parser.expects(:parse) + @code.perform_initial_import + end + + it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do + File.stubs(:expand_path).with("/my/file").returns "/my/file" + File.expects(:exist?).with("/my/file").returns true + Puppet.settings[:manifest] = "/my/file" + @parser.expects(:file=).with "/my/file" + @parser.expects(:parse) + @code.perform_initial_import + end + + it "should not attempt to load a manifest if none is present" do + File.stubs(:expand_path).with("/my/file").returns "/my/file" + File.expects(:exist?).with("/my/file").returns false + Puppet.settings[:manifest] = "/my/file" + @parser.expects(:file=).never + @parser.expects(:parse).never + @code.perform_initial_import + end + + it "should fail helpfully if there is an error importing" do + File.stubs(:exist?).returns true + @parser.expects(:parse).raises ArgumentError + lambda { @code.perform_initial_import }.should raise_error(Puppet::Error) + end + end + + describe "when determining the configuration version" do + before do + @code = Puppet::Resource::TypeCollection.new("env") + end + + it "should default to the current time" do + time = Time.now + + Time.stubs(:now).returns time + @code.version.should == time.to_i + end + + it "should use the output of the environment's config_version setting if one is provided" do + @code.environment.stubs(:[]).with(:config_version).returns("/my/foo") + + Puppet::Util.expects(:execute).with(["/my/foo"]).returns "output\n" + @code.version.should == "output" + end + + it "should raise a puppet parser error if executing config_version fails" do + @code.environment.stubs(:[]).with(:config_version).returns("test") + Puppet::Util.expects(:execute).raises(Puppet::ExecutionFailure.new("msg")) + + lambda { @code.version }.should raise_error(Puppet::ParseError) + end + + end +end diff --git a/spec/unit/resource/type_collection_spec_spec.rb b/spec/unit/resource/type_collection_spec_spec.rb deleted file mode 100644 index c0711bb87..000000000 --- a/spec/unit/resource/type_collection_spec_spec.rb +++ /dev/null @@ -1,432 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/resource/type_collection' -require 'puppet/resource/type' - -describe Puppet::Resource::TypeCollection do - before do - @instance = Puppet::Resource::Type.new(:hostclass, "foo") - @code = Puppet::Resource::TypeCollection.new("env") - end - - it "should require an environment at initialization" do - env = Puppet::Node::Environment.new("testing") - Puppet::Resource::TypeCollection.new(env).environment.should equal(env) - end - - it "should convert the environment into an environment instance if a string is provided" do - env = Puppet::Node::Environment.new("testing") - Puppet::Resource::TypeCollection.new("testing").environment.should equal(env) - end - - it "should create a 'loader' at initialization" do - Puppet::Resource::TypeCollection.new("testing").loader.should be_instance_of(Puppet::Parser::TypeLoader) - end - - it "should be able to add a resource type" do - Puppet::Resource::TypeCollection.new("env").should respond_to(:add) - end - - it "should consider '<<' to be an alias to 'add' but should return self" do - loader = Puppet::Resource::TypeCollection.new("env") - loader.expects(:add).with "foo" - loader.expects(:add).with "bar" - loader << "foo" << "bar" - end - - it "should set itself as the code collection for added resource types" do - loader = Puppet::Resource::TypeCollection.new("env") - - node = Puppet::Resource::Type.new(:node, "foo") - - @code.add(node) - @code.node("foo").should equal(node) - - node.resource_type_collection.should equal(@code) - end - - it "should store node resource types as nodes" do - node = Puppet::Resource::Type.new(:node, "foo") - - @code.add(node) - @code.node("foo").should equal(node) - end - - it "should store hostclasses as hostclasses" do - klass = Puppet::Resource::Type.new(:hostclass, "foo") - - @code.add(klass) - @code.hostclass("foo").should equal(klass) - end - - it "should store definitions as definitions" do - define = Puppet::Resource::Type.new(:definition, "foo") - - @code.add(define) - @code.definition("foo").should equal(define) - end - - it "should merge new classes with existing classes of the same name" do - loader = Puppet::Resource::TypeCollection.new("env") - first = Puppet::Resource::Type.new(:hostclass, "foo") - second = Puppet::Resource::Type.new(:hostclass, "foo") - loader.add first - first.expects(:merge).with(second) - loader.add(second) - end - - it "should remove all nodes, classes, and definitions when cleared" do - loader = Puppet::Resource::TypeCollection.new("env") - loader.add Puppet::Resource::Type.new(:hostclass, "class") - loader.add Puppet::Resource::Type.new(:definition, "define") - loader.add Puppet::Resource::Type.new(:node, "node") - - loader.clear - loader.hostclass("class").should be_nil - loader.definition("define").should be_nil - loader.node("node").should be_nil - end - - describe "when looking up names" do - before do - @type = Puppet::Resource::Type.new(:hostclass, "ns::klass") - end - - it "should support looking up with multiple namespaces" do - @code.add @type - @code.find_hostclass(%w{boo baz ns}, "klass").should equal(@type) - end - - it "should not attempt to import anything when the type is already defined" do - @code.add @type - @code.loader.expects(:import).never - @code.find_hostclass(%w{ns}, "klass").should equal(@type) - end - - describe "that need to be loaded" do - it "should use the loader to load the files" do - @code.loader.expects(:load_until).with(["ns"], "klass") - @code.find_or_load(["ns"], "klass", :hostclass) - end - - it "should downcase the name and downcase and array-fy the namespaces before passing to the loader" do - @code.loader.expects(:load_until).with(["ns"], "klass") - @code.find_or_load("Ns", "Klass", :hostclass) - end - - it "should attempt to find the type when the loader yields" do - @code.loader.expects(:load_until).yields - @code.expects(:find).with(["ns"], "klass", :hostclass).times(2).returns(false).then.returns(true) - @code.find_or_load("ns", "klass", :hostclass) - end - - it "should return the result of 'load_until'" do - @code.loader.expects(:load_until).returns "foo" - @code.find_or_load("Ns", "Klass", :hostclass).should == "foo" - end - - it "should return nil if the name isn't found" do - @code.stubs(:load_until).returns(nil) - @code.find_or_load("Ns", "Klass", :hostclass).should be_nil - end - end - end - - %w{hostclass node definition}.each do |data| - before do - @instance = Puppet::Resource::Type.new(data, "foo") - end - - it "should have a method for adding a #{data}" do - Puppet::Resource::TypeCollection.new("env").should respond_to("add_" + data) - end - - it "should use the name of the instance to add it" do - loader = Puppet::Resource::TypeCollection.new("env") - loader.send("add_#{data}", @instance) - loader.send(data, @instance.name).should equal(@instance) - end - - unless data == "hostclass" - it "should fail to add a #{data} when one already exists" do - loader = Puppet::Resource::TypeCollection.new("env") - loader.add @instance - lambda { loader.add(@instance) }.should raise_error(Puppet::ParseError) - end - end - - it "should return the added #{data}" do - loader = Puppet::Resource::TypeCollection.new("env") - - loader.add(@instance).should equal(@instance) - end - - it "should be able to retrieve #{data} by name" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(data, "bar") - loader.add instance - loader.send(data, "bar").should equal(instance) - end - - it "should retrieve #{data} insensitive to case" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(data, "Bar") - loader.add instance - loader.send(data, "bAr").should equal(instance) - end - - it "should return nil when asked for a #{data} that has not been added" do - Puppet::Resource::TypeCollection.new("env").send(data, "foo").should be_nil - end - - it "should be able to retrieve all #{data}s" do - plurals = { "hostclass" => "hostclasses", "node" => "nodes", "definition" => "definitions" } - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(data, "foo") - loader.add instance - loader.send(plurals[data]).should == { "foo" => instance } - end - end - - describe "when finding a qualified instance" do - it "should return any found instance if the instance name is fully qualified" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") - loader.add instance - loader.find("namespace", "::foo::bar", :hostclass).should equal(instance) - end - - it "should return nil if the instance name is fully qualified and no such instance exists" do - loader = Puppet::Resource::TypeCollection.new("env") - loader.find("namespace", "::foo::bar", :hostclass).should be_nil - end - - it "should be able to find classes in the base namespace" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(:hostclass, "foo") - loader.add instance - loader.find("", "foo", :hostclass).should equal(instance) - end - - it "should return the partially qualified object if it exists in a provided namespace" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") - loader.add instance - loader.find("foo", "bar::baz", :hostclass).should equal(instance) - end - - it "should be able to find partially qualified objects in any of the provided namespaces" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") - loader.add instance - loader.find(["nons", "foo", "otherns"], "bar::baz", :hostclass).should equal(instance) - end - - it "should return the unqualified object if it exists in a provided namespace" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") - loader.add instance - loader.find("foo", "bar", :hostclass).should equal(instance) - end - - it "should return the unqualified object if it exists in the parent namespace" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") - loader.add instance - loader.find("foo::bar::baz", "bar", :hostclass).should equal(instance) - end - - it "should should return the partially qualified object if it exists in the parent namespace" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") - loader.add instance - loader.find("foo::bar", "bar::baz", :hostclass).should equal(instance) - end - - it "should return the qualified object if it exists in the root namespace" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") - loader.add instance - loader.find("foo::bar", "foo::bar::baz", :hostclass).should equal(instance) - end - - it "should return nil if the object cannot be found" do - loader = Puppet::Resource::TypeCollection.new("env") - instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") - loader.add instance - loader.find("foo::bar", "eh", :hostclass).should be_nil - end - end - - it "should use the generic 'find' method with an empty namespace to find nodes" do - loader = Puppet::Resource::TypeCollection.new("env") - loader.expects(:find).with("", "bar", :node) - loader.find_node("bar") - end - - it "should use the 'find_or_load' method to find hostclasses" do - loader = Puppet::Resource::TypeCollection.new("env") - loader.expects(:find_or_load).with("foo", "bar", :hostclass) - loader.find_hostclass("foo", "bar") - end - - it "should use the 'find_or_load' method to find definitions" do - loader = Puppet::Resource::TypeCollection.new("env") - loader.expects(:find_or_load).with("foo", "bar", :definition) - loader.find_definition("foo", "bar") - end - - it "should indicate whether any nodes are defined" do - loader = Puppet::Resource::TypeCollection.new("env") - loader.add_node(Puppet::Resource::Type.new(:node, "foo")) - loader.should be_nodes - end - - it "should indicate whether no nodes are defined" do - Puppet::Resource::TypeCollection.new("env").should_not be_nodes - end - - describe "when finding nodes" do - before :each do - @loader = Puppet::Resource::TypeCollection.new("env") - end - - it "should return any node whose name exactly matches the provided node name" do - node = Puppet::Resource::Type.new(:node, "foo") - @loader << node - - @loader.node("foo").should equal(node) - end - - it "should return the first regex node whose regex matches the provided node name" do - node1 = Puppet::Resource::Type.new(:node, /\w/) - node2 = Puppet::Resource::Type.new(:node, /\d/) - @loader << node1 << node2 - - @loader.node("foo10").should equal(node1) - end - - it "should preferentially return a node whose name is string-equal over returning a node whose regex matches a provided name" do - node1 = Puppet::Resource::Type.new(:node, /\w/) - node2 = Puppet::Resource::Type.new(:node, "foo") - @loader << node1 << node2 - - @loader.node("foo").should equal(node2) - end - end - - describe "when managing files" do - before do - @loader = Puppet::Resource::TypeCollection.new("env") - Puppet::Util::LoadedFile.stubs(:new).returns stub("watched_file") - end - - it "should have a method for specifying a file should be watched" do - @loader.should respond_to(:watch_file) - end - - it "should have a method for determining if a file is being watched" do - @loader.watch_file("/foo/bar") - @loader.should be_watching_file("/foo/bar") - end - - it "should use LoadedFile to watch files" do - Puppet::Util::LoadedFile.expects(:new).with("/foo/bar").returns stub("watched_file") - @loader.watch_file("/foo/bar") - end - - it "should be considered stale if any files have changed" do - file1 = stub 'file1', :changed? => false - file2 = stub 'file2', :changed? => true - Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) - @loader.watch_file("/foo/bar") - @loader.watch_file("/other/bar") - - @loader.should be_stale - end - - it "should not be considered stable if no files have changed" do - file1 = stub 'file1', :changed? => false - file2 = stub 'file2', :changed? => false - Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) - @loader.watch_file("/foo/bar") - @loader.watch_file("/other/bar") - - @loader.should_not be_stale - end - end - - describe "when performing initial import" do - before do - @parser = stub 'parser', :file= => nil, :string => nil, :parse => nil - Puppet::Parser::Parser.stubs(:new).returns @parser - @code = Puppet::Resource::TypeCollection.new("env") - end - - it "should create a new parser instance" do - Puppet::Parser::Parser.expects(:new).returns @parser - @code.perform_initial_import - end - - it "should set the parser's string to the 'code' setting and parse if code is available" do - Puppet.settings[:code] = "my code" - @parser.expects(:string=).with "my code" - @parser.expects(:parse) - @code.perform_initial_import - end - - it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do - File.stubs(:expand_path).with("/my/file").returns "/my/file" - File.expects(:exist?).with("/my/file").returns true - Puppet.settings[:manifest] = "/my/file" - @parser.expects(:file=).with "/my/file" - @parser.expects(:parse) - @code.perform_initial_import - end - - it "should not attempt to load a manifest if none is present" do - File.stubs(:expand_path).with("/my/file").returns "/my/file" - File.expects(:exist?).with("/my/file").returns false - Puppet.settings[:manifest] = "/my/file" - @parser.expects(:file=).never - @parser.expects(:parse).never - @code.perform_initial_import - end - - it "should fail helpfully if there is an error importing" do - File.stubs(:exist?).returns true - @parser.expects(:parse).raises ArgumentError - lambda { @code.perform_initial_import }.should raise_error(Puppet::Error) - end - end - - describe "when determining the configuration version" do - before do - @code = Puppet::Resource::TypeCollection.new("env") - end - - it "should default to the current time" do - time = Time.now - - Time.stubs(:now).returns time - @code.version.should == time.to_i - end - - it "should use the output of the environment's config_version setting if one is provided" do - @code.environment.stubs(:[]).with(:config_version).returns("/my/foo") - - Puppet::Util.expects(:execute).with(["/my/foo"]).returns "output\n" - @code.version.should == "output" - end - - it "should raise a puppet parser error if executing config_version fails" do - @code.environment.stubs(:[]).with(:config_version).returns("test") - Puppet::Util.expects(:execute).raises(Puppet::ExecutionFailure.new("msg")) - - lambda { @code.version }.should raise_error(Puppet::ParseError) - end - - end -end diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb new file mode 100755 index 000000000..ca9daf8c6 --- /dev/null +++ b/spec/unit/resource/type_spec.rb @@ -0,0 +1,701 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/resource/type' + +describe Puppet::Resource::Type do + it "should have a 'name' attribute" do + Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo" + end + + [:code, :doc, :line, :file, :resource_type_collection, :ruby_code].each do |attr| + it "should have a '#{attr}' attribute" do + type = Puppet::Resource::Type.new(:hostclass, "foo") + type.send(attr.to_s + "=", "yay") + type.send(attr).should == "yay" + end + end + + [:hostclass, :node, :definition].each do |type| + it "should know when it is a #{type}" do + Puppet::Resource::Type.new(type, "foo").send("#{type}?").should be_true + end + end + + it "should indirect 'resource_type'" do + Puppet::Resource::Type.indirection.name.should == :resource_type + end + + it "should default to 'parser' for its terminus class" do + Puppet::Resource::Type.indirection.terminus_class.should == :parser + end + + describe "when converting to json" do + before do + @type = Puppet::Resource::Type.new(:hostclass, "foo") + end + + def from_json(json) + Puppet::Resource::Type.from_pson(json) + end + + def double_convert + Puppet::Resource::Type.from_pson(PSON.parse(@type.to_pson)) + end + + it "should include the name and type" do + double_convert.name.should == @type.name + double_convert.type.should == @type.type + end + + it "should include any arguments" do + @type.set_arguments("one" => nil, "two" => "foo") + + double_convert.arguments.should == {"one" => nil, "two" => "foo"} + end + + it "should include any extra attributes" do + @type.file = "/my/file" + @type.line = 50 + + double_convert.file.should == "/my/file" + double_convert.line.should == 50 + end + end + + describe "when a node" do + it "should allow a regex as its name" do + lambda { Puppet::Resource::Type.new(:node, /foo/) }.should_not raise_error + end + + it "should allow a AST::HostName instance as its name" do + regex = Puppet::Parser::AST::Regex.new(:value => /foo/) + name = Puppet::Parser::AST::HostName.new(:value => regex) + lambda { Puppet::Resource::Type.new(:node, name) }.should_not raise_error + end + + it "should match against the regexp in the AST::HostName when a HostName instance is provided" do + regex = Puppet::Parser::AST::Regex.new(:value => /\w/) + name = Puppet::Parser::AST::HostName.new(:value => regex) + node = Puppet::Resource::Type.new(:node, name) + + node.match("foo").should be_true + end + + it "should return the value of the hostname if provided a string-form AST::HostName instance as the name" do + name = Puppet::Parser::AST::HostName.new(:value => "foo") + node = Puppet::Resource::Type.new(:node, name) + + node.name.should == "foo" + end + + describe "and the name is a regex" do + it "should have a method that indicates that this is the case" do + Puppet::Resource::Type.new(:node, /w/).should be_name_is_regex + end + + it "should set its namespace to ''" do + Puppet::Resource::Type.new(:node, /w/).namespace.should == "" + end + + it "should return the regex converted to a string when asked for its name" do + Puppet::Resource::Type.new(:node, /ww/).name.should == "ww" + end + + it "should downcase the regex when returning the name as a string" do + Puppet::Resource::Type.new(:node, /W/).name.should == "w" + end + + it "should remove non-alpha characters when returning the name as a string" do + Puppet::Resource::Type.new(:node, /w*w/).name.should_not include("*") + end + + it "should remove leading dots when returning the name as a string" do + Puppet::Resource::Type.new(:node, /.ww/).name.should_not =~ /^\./ + end + + it "should have a method for matching its regex name against a provided name" do + Puppet::Resource::Type.new(:node, /.ww/).should respond_to(:match) + end + + it "should return true when its regex matches the provided name" do + Puppet::Resource::Type.new(:node, /\w/).match("foo").should be_true + end + + it "should return false when its regex does not match the provided name" do + (!!Puppet::Resource::Type.new(:node, /\d/).match("foo")).should be_false + end + + it "should return true when its name, as a string, is matched against an equal string" do + Puppet::Resource::Type.new(:node, "foo").match("foo").should be_true + end + + it "should return false when its name is matched against an unequal string" do + Puppet::Resource::Type.new(:node, "foo").match("bar").should be_false + end + + it "should match names insensitive to case" do + Puppet::Resource::Type.new(:node, "fOo").match("foO").should be_true + end + end + + it "should return the name converted to a string when the name is not a regex" do + pending "Need to define LoadedCode behaviour first" + name = Puppet::Parser::AST::HostName.new(:value => "foo") + Puppet::Resource::Type.new(:node, name).name.should == "foo" + end + + it "should return the name converted to a string when the name is a regex" do + pending "Need to define LoadedCode behaviour first" + name = Puppet::Parser::AST::HostName.new(:value => /regex/) + Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s + end + + it "should mark any created scopes as a node scope" do + pending "Need to define LoadedCode behaviour first" + name = Puppet::Parser::AST::HostName.new(:value => /regex/) + Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s + end + end + + describe "when initializing" do + it "should require a resource super type" do + Puppet::Resource::Type.new(:hostclass, "foo").type.should == :hostclass + end + + it "should fail if provided an invalid resource super type" do + lambda { Puppet::Resource::Type.new(:nope, "foo") }.should raise_error(ArgumentError) + end + + it "should set its name to the downcased, stringified provided name" do + Puppet::Resource::Type.new(:hostclass, "Foo::Bar".intern).name.should == "foo::bar" + end + + it "should set its namespace to the downcased, stringified qualified name for classes" do + Puppet::Resource::Type.new(:hostclass, "Foo::Bar::Baz".intern).namespace.should == "foo::bar::baz" + end + + [:definition, :node].each do |type| + it "should set its namespace to the downcased, stringified qualified portion of the name for #{type}s" do + Puppet::Resource::Type.new(type, "Foo::Bar::Baz".intern).namespace.should == "foo::bar" + end + end + + %w{code line file doc}.each do |arg| + it "should set #{arg} if provided" do + type = Puppet::Resource::Type.new(:hostclass, "foo", arg.to_sym => "something") + type.send(arg).should == "something" + end + end + + it "should set any provided arguments with the keys as symbols" do + type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {:foo => "bar", :baz => "biz"}) + type.should be_valid_parameter("foo") + type.should be_valid_parameter("baz") + end + + it "should set any provided arguments with they keys as strings" do + type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar", "baz" => "biz"}) + type.should be_valid_parameter(:foo) + type.should be_valid_parameter(:baz) + end + + it "should function if provided no arguments" do + type = Puppet::Resource::Type.new(:hostclass, "foo") + type.should_not be_valid_parameter(:foo) + end + end + + describe "when testing the validity of an attribute" do + it "should return true if the parameter was typed at initialization" do + Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar"}).should be_valid_parameter("foo") + end + + it "should return true if it is a metaparam" do + Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("require") + end + + it "should return true if the parameter is named 'name'" do + Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("name") + end + + it "should return false if it is not a metaparam and was not provided at initialization" do + Puppet::Resource::Type.new(:hostclass, "foo").should_not be_valid_parameter("yayness") + end + end + + describe "when creating a subscope" do + before do + @scope = stub 'scope', :newscope => nil + @resource = stub 'resource' + @type = Puppet::Resource::Type.new(:hostclass, "foo") + end + + it "should return a new scope created with the provided scope as the parent" do + @scope.expects(:newscope).returns "foo" + @type.subscope(@scope, @resource).should == "foo" + end + + it "should set the source as itself" do + @scope.expects(:newscope).with { |args| args[:source] == @type } + @type.subscope(@scope, @resource) + end + + it "should set the scope's namespace to its namespace" do + @type.expects(:namespace).returns "yayness" + @scope.expects(:newscope).with { |args| args[:namespace] == "yayness" } + @type.subscope(@scope, @resource) + end + + it "should set the scope's resource to the provided resource" do + @scope.expects(:newscope).with { |args| args[:resource] == @resource } + @type.subscope(@scope, @resource) + end + end + + describe "when setting its parameters in the scope" do + before do + @scope = Puppet::Parser::Scope.new(:compiler => stub("compiler", :environment => Puppet::Node::Environment.new), :source => stub("source")) + @resource = Puppet::Parser::Resource.new(:foo, "bar", :scope => @scope) + @type = Puppet::Resource::Type.new(:hostclass, "foo") + end + + it "should set each of the resource's parameters as variables in the scope" do + @type.set_arguments :foo => nil, :boo => nil + @resource[:foo] = "bar" + @resource[:boo] = "baz" + + @type.set_resource_parameters(@resource, @scope) + + @scope.lookupvar("foo").should == "bar" + @scope.lookupvar("boo").should == "baz" + end + + it "should set the variables as strings" do + @type.set_arguments :foo => nil + @resource[:foo] = "bar" + + @type.set_resource_parameters(@resource, @scope) + + @scope.lookupvar("foo").should == "bar" + end + + it "should fail if any of the resource's parameters are not valid attributes" do + @type.set_arguments :foo => nil + @resource[:boo] = "baz" + + lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError) + end + + it "should evaluate and set its default values as variables for parameters not provided by the resource" do + @type.set_arguments :foo => stub("value", :safeevaluate => "something") + @type.set_resource_parameters(@resource, @scope) + @scope.lookupvar("foo").should == "something" + end + + it "should set all default values as parameters in the resource" do + @type.set_arguments :foo => stub("value", :safeevaluate => "something") + + @type.set_resource_parameters(@resource, @scope) + + @resource[:foo].should == "something" + end + + it "should fail if the resource does not provide a value for a required argument" do + @type.set_arguments :foo => nil + @resource.expects(:to_hash).returns({}) + + lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError) + end + + it "should set the resource's title as a variable if not otherwise provided" do + @type.set_resource_parameters(@resource, @scope) + + @scope.lookupvar("title").should == "bar" + end + + it "should set the resource's name as a variable if not otherwise provided" do + @type.set_resource_parameters(@resource, @scope) + + @scope.lookupvar("name").should == "bar" + end + + it "should set its module name in the scope if available" do + @type.module_name = "mymod" + + @type.set_resource_parameters(@resource, @scope) + + @scope.lookupvar("module_name").should == "mymod" + end + + it "should set its caller module name in the scope if available" do + @scope.expects(:parent_module_name).returns "mycaller" + + @type.set_resource_parameters(@resource, @scope) + + @scope.lookupvar("caller_module_name").should == "mycaller" + end + end + + describe "when describing and managing parent classes" do + before do + @code = Puppet::Resource::TypeCollection.new("env") + @parent = Puppet::Resource::Type.new(:hostclass, "bar") + @code.add @parent + + @child = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") + @code.add @child + end + + it "should be able to define a parent" do + Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") + end + + it "should use the code collection to find the parent resource type" do + @child.parent_type.should equal(@parent) + end + + it "should be able to find parent nodes" do + parent = Puppet::Resource::Type.new(:node, "bar") + @code.add parent + child = Puppet::Resource::Type.new(:node, "foo", :parent => "bar") + @code.add child + + child.parent_type.should equal(parent) + end + + it "should cache a reference to the parent type" do + @code.expects(:hostclass).once.with("bar").returns @parent + @child.parent_type + @child.parent_type + end + + it "should correctly state when it is another type's child" do + @child.should be_child_of(@parent) + end + + it "should be considered the child of a parent's parent" do + @grandchild = Puppet::Resource::Type.new(:hostclass, "baz", :parent => "foo") + @code.add @grandchild + + @grandchild.should be_child_of(@parent) + end + + it "should correctly state when it is not another type's child" do + @notchild = Puppet::Resource::Type.new(:hostclass, "baz") + @code.add @notchild + + @notchild.should_not be_child_of(@parent) + end + end + + describe "when evaluating its code" do + before do + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) + @scope = Puppet::Parser::Scope.new :compiler => @compiler + @resource = Puppet::Parser::Resource.new(:foo, "yay", :scope => @scope) + + # This is so the internal resource lookup works, yo. + @compiler.catalog.add_resource @resource + + @known_resource_types = stub 'known_resource_types' + @resource.stubs(:known_resource_types).returns @known_resource_types + @type = Puppet::Resource::Type.new(:hostclass, "foo") + end + + it "should set all of its parameters in a subscope" do + subscope = stub 'subscope', :compiler => @compiler + @type.expects(:subscope).with(@scope, @resource).returns subscope + @type.expects(:set_resource_parameters).with(@resource, subscope) + + @type.evaluate_code(@resource) + end + + it "should not create a subscope for the :main class" do + @resource.stubs(:title).returns(:main) + @type.expects(:subscope).never + @type.expects(:set_resource_parameters).with(@resource, @scope) + + @type.evaluate_code(@resource) + end + + it "should store the class scope" do + @type.evaluate_code(@resource) + @scope.class_scope(@type).should be_instance_of(@scope.class) + end + + it "should still create a scope but not store it if the type is a definition" do + @type = Puppet::Resource::Type.new(:definition, "foo") + @type.evaluate_code(@resource) + @scope.class_scope(@type).should be_nil + end + + it "should evaluate the AST code if any is provided" do + code = stub 'code' + @type.stubs(:code).returns code + @type.stubs(:subscope).returns stub_everything("subscope", :compiler => @compiler) + code.expects(:safeevaluate).with @type.subscope + + @type.evaluate_code(@resource) + end + + describe "and ruby code is provided" do + it "should create a DSL Resource API and evaluate it" do + @type.stubs(:ruby_code).returns(proc { "foo" }) + @api = stub 'api' + Puppet::DSL::ResourceAPI.expects(:new).with { |res, scope, code| code == @type.ruby_code }.returns @api + @api.expects(:evaluate) + + @type.evaluate_code(@resource) + end + end + + it "should noop if there is no code" do + @type.expects(:code).returns nil + + @type.evaluate_code(@resource) + end + + describe "and it has a parent class" do + before do + @parent_type = Puppet::Resource::Type.new(:hostclass, "parent") + @type.parent = "parent" + @parent_resource = Puppet::Parser::Resource.new(:class, "parent", :scope => @scope) + + @compiler.add_resource @scope, @parent_resource + + @type.resource_type_collection = @scope.known_resource_types + @type.resource_type_collection.add @parent_type + end + + it "should evaluate the parent's resource" do + @type.evaluate_code(@resource) + + @scope.class_scope(@parent_type).should_not be_nil + end + + it "should not evaluate the parent's resource if it has already been evaluated" do + @parent_resource.evaluate + + @parent_resource.expects(:evaluate).never + + @type.evaluate_code(@resource) + end + + it "should use the parent's scope as its base scope" do + @type.evaluate_code(@resource) + + @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id + end + end + + describe "and it has a parent node" do + before do + @type = Puppet::Resource::Type.new(:node, "foo") + @parent_type = Puppet::Resource::Type.new(:node, "parent") + @type.parent = "parent" + @parent_resource = Puppet::Parser::Resource.new(:node, "parent", :scope => @scope) + + @compiler.add_resource @scope, @parent_resource + + @type.resource_type_collection = @scope.known_resource_types + @type.resource_type_collection.stubs(:node).with("parent").returns(@parent_type) + @type.resource_type_collection.stubs(:node).with("Parent").returns(@parent_type) + end + + it "should evaluate the parent's resource" do + @type.evaluate_code(@resource) + + @scope.class_scope(@parent_type).should_not be_nil + end + + it "should not evaluate the parent's resource if it has already been evaluated" do + @parent_resource.evaluate + + @parent_resource.expects(:evaluate).never + + @type.evaluate_code(@resource) + end + + it "should use the parent's scope as its base scope" do + @type.evaluate_code(@resource) + + @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id + end + end + end + + describe "when creating a resource" do + before do + @node = Puppet::Node.new("foo") + @compiler = Puppet::Parser::Compiler.new(@node) + @scope = Puppet::Parser::Scope.new(:compiler => @compiler) + + @top = Puppet::Resource::Type.new :hostclass, "top" + @middle = Puppet::Resource::Type.new :hostclass, "middle", :parent => "top" + + @code = Puppet::Resource::TypeCollection.new("env") + @code.add @top + @code.add @middle + end + + it "should create a resource instance" do + @top.mk_plain_resource(@scope).should be_instance_of(Puppet::Parser::Resource) + end + + it "should set its resource type to 'class' when it is a hostclass" do + Puppet::Resource::Type.new(:hostclass, "top").mk_plain_resource(@scope).type.should == "Class" + end + + it "should set its resource type to 'node' when it is a node" do + Puppet::Resource::Type.new(:node, "top").mk_plain_resource(@scope).type.should == "Node" + end + + it "should fail when it is a definition" do + lambda { Puppet::Resource::Type.new(:definition, "top").mk_plain_resource(@scope) }.should raise_error(ArgumentError) + end + + it "should add the created resource to the scope's catalog" do + @top.mk_plain_resource(@scope) + + @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) + end + + it "should evaluate the parent class if one exists" do + @middle.mk_plain_resource(@scope) + + @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) + end + + it "should fail to evaluate if a parent class is defined but cannot be found" do + othertop = Puppet::Resource::Type.new :hostclass, "something", :parent => "yay" + @code.add othertop + lambda { othertop.mk_plain_resource(@scope) }.should raise_error(Puppet::ParseError) + end + + it "should not create a new resource if one already exists" do + @compiler.catalog.expects(:resource).with(:class, "top").returns("something") + @compiler.catalog.expects(:add_resource).never + @top.mk_plain_resource(@scope) + end + + it "should return the existing resource when not creating a new one" do + @compiler.catalog.expects(:resource).with(:class, "top").returns("something") + @compiler.catalog.expects(:add_resource).never + @top.mk_plain_resource(@scope).should == "something" + end + + it "should not create a new parent resource if one already exists and it has a parent class" do + @top.mk_plain_resource(@scope) + + top_resource = @compiler.catalog.resource(:class, "top") + + @middle.mk_plain_resource(@scope) + + @compiler.catalog.resource(:class, "top").should equal(top_resource) + end + + # #795 - tag before evaluation. + it "should tag the catalog with the resource tags when it is evaluated" do + @middle.mk_plain_resource(@scope) + + @compiler.catalog.should be_tagged("middle") + end + + it "should tag the catalog with the parent class tags when it is evaluated" do + @middle.mk_plain_resource(@scope) + + @compiler.catalog.should be_tagged("top") + end + end + + describe "when merging code from another instance" do + def code(str) + Puppet::Parser::AST::Leaf.new :value => str + end + + it "should fail unless it is a class" do + lambda { Puppet::Resource::Type.new(:node, "bar").merge("foo") }.should raise_error(Puppet::Error) + end + + it "should fail unless the source instance is a class" do + dest = Puppet::Resource::Type.new(:hostclass, "bar") + source = Puppet::Resource::Type.new(:node, "foo") + lambda { dest.merge(source) }.should raise_error(Puppet::Error) + end + + it "should fail if both classes have different parent classes" do + code = Puppet::Resource::TypeCollection.new("env") + {"a" => "b", "c" => "d"}.each do |parent, child| + code.add Puppet::Resource::Type.new(:hostclass, parent) + code.add Puppet::Resource::Type.new(:hostclass, child, :parent => parent) + end + lambda { code.hostclass("b").merge(code.hostclass("d")) }.should raise_error(Puppet::Error) + end + + it "should fail if it's named 'main' and 'freeze_main' is enabled" do + Puppet.settings[:freeze_main] = true + code = Puppet::Resource::TypeCollection.new("env") + code.add Puppet::Resource::Type.new(:hostclass, "") + other = Puppet::Resource::Type.new(:hostclass, "") + lambda { code.hostclass("").merge(other) }.should raise_error(Puppet::Error) + end + + it "should copy the other class's parent if it has not parent" do + dest = Puppet::Resource::Type.new(:hostclass, "bar") + + parent = Puppet::Resource::Type.new(:hostclass, "parent") + source = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "parent") + dest.merge(source) + + dest.parent.should == "parent" + end + + it "should copy the other class's documentation as its docs if it has no docs" do + dest = Puppet::Resource::Type.new(:hostclass, "bar") + source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness") + dest.merge(source) + + dest.doc.should == "yayness" + end + + it "should append the other class's docs to its docs if it has any" do + dest = Puppet::Resource::Type.new(:hostclass, "bar", :doc => "fooness") + source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness") + dest.merge(source) + + dest.doc.should == "foonessyayness" + end + + it "should turn its code into an ASTArray if necessary" do + dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => code("foo")) + source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar")) + + dest.merge(source) + + dest.code.should be_instance_of(Puppet::Parser::AST::ASTArray) + end + + it "should set the other class's code as its code if it has none" do + dest = Puppet::Resource::Type.new(:hostclass, "bar") + source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar")) + + dest.merge(source) + + dest.code.value.should == "bar" + end + + it "should append the other class's code to its code if it has any" do + dcode = Puppet::Parser::AST::ASTArray.new :children => [code("dest")] + dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => dcode) + + scode = Puppet::Parser::AST::ASTArray.new :children => [code("source")] + source = Puppet::Resource::Type.new(:hostclass, "foo", :code => scode) + + dest.merge(source) + + dest.code.children.collect { |l| l.value }.should == %w{dest source} + end + end +end diff --git a/spec/unit/resource/type_spec_spec.rb b/spec/unit/resource/type_spec_spec.rb deleted file mode 100755 index ca9daf8c6..000000000 --- a/spec/unit/resource/type_spec_spec.rb +++ /dev/null @@ -1,701 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/resource/type' - -describe Puppet::Resource::Type do - it "should have a 'name' attribute" do - Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo" - end - - [:code, :doc, :line, :file, :resource_type_collection, :ruby_code].each do |attr| - it "should have a '#{attr}' attribute" do - type = Puppet::Resource::Type.new(:hostclass, "foo") - type.send(attr.to_s + "=", "yay") - type.send(attr).should == "yay" - end - end - - [:hostclass, :node, :definition].each do |type| - it "should know when it is a #{type}" do - Puppet::Resource::Type.new(type, "foo").send("#{type}?").should be_true - end - end - - it "should indirect 'resource_type'" do - Puppet::Resource::Type.indirection.name.should == :resource_type - end - - it "should default to 'parser' for its terminus class" do - Puppet::Resource::Type.indirection.terminus_class.should == :parser - end - - describe "when converting to json" do - before do - @type = Puppet::Resource::Type.new(:hostclass, "foo") - end - - def from_json(json) - Puppet::Resource::Type.from_pson(json) - end - - def double_convert - Puppet::Resource::Type.from_pson(PSON.parse(@type.to_pson)) - end - - it "should include the name and type" do - double_convert.name.should == @type.name - double_convert.type.should == @type.type - end - - it "should include any arguments" do - @type.set_arguments("one" => nil, "two" => "foo") - - double_convert.arguments.should == {"one" => nil, "two" => "foo"} - end - - it "should include any extra attributes" do - @type.file = "/my/file" - @type.line = 50 - - double_convert.file.should == "/my/file" - double_convert.line.should == 50 - end - end - - describe "when a node" do - it "should allow a regex as its name" do - lambda { Puppet::Resource::Type.new(:node, /foo/) }.should_not raise_error - end - - it "should allow a AST::HostName instance as its name" do - regex = Puppet::Parser::AST::Regex.new(:value => /foo/) - name = Puppet::Parser::AST::HostName.new(:value => regex) - lambda { Puppet::Resource::Type.new(:node, name) }.should_not raise_error - end - - it "should match against the regexp in the AST::HostName when a HostName instance is provided" do - regex = Puppet::Parser::AST::Regex.new(:value => /\w/) - name = Puppet::Parser::AST::HostName.new(:value => regex) - node = Puppet::Resource::Type.new(:node, name) - - node.match("foo").should be_true - end - - it "should return the value of the hostname if provided a string-form AST::HostName instance as the name" do - name = Puppet::Parser::AST::HostName.new(:value => "foo") - node = Puppet::Resource::Type.new(:node, name) - - node.name.should == "foo" - end - - describe "and the name is a regex" do - it "should have a method that indicates that this is the case" do - Puppet::Resource::Type.new(:node, /w/).should be_name_is_regex - end - - it "should set its namespace to ''" do - Puppet::Resource::Type.new(:node, /w/).namespace.should == "" - end - - it "should return the regex converted to a string when asked for its name" do - Puppet::Resource::Type.new(:node, /ww/).name.should == "ww" - end - - it "should downcase the regex when returning the name as a string" do - Puppet::Resource::Type.new(:node, /W/).name.should == "w" - end - - it "should remove non-alpha characters when returning the name as a string" do - Puppet::Resource::Type.new(:node, /w*w/).name.should_not include("*") - end - - it "should remove leading dots when returning the name as a string" do - Puppet::Resource::Type.new(:node, /.ww/).name.should_not =~ /^\./ - end - - it "should have a method for matching its regex name against a provided name" do - Puppet::Resource::Type.new(:node, /.ww/).should respond_to(:match) - end - - it "should return true when its regex matches the provided name" do - Puppet::Resource::Type.new(:node, /\w/).match("foo").should be_true - end - - it "should return false when its regex does not match the provided name" do - (!!Puppet::Resource::Type.new(:node, /\d/).match("foo")).should be_false - end - - it "should return true when its name, as a string, is matched against an equal string" do - Puppet::Resource::Type.new(:node, "foo").match("foo").should be_true - end - - it "should return false when its name is matched against an unequal string" do - Puppet::Resource::Type.new(:node, "foo").match("bar").should be_false - end - - it "should match names insensitive to case" do - Puppet::Resource::Type.new(:node, "fOo").match("foO").should be_true - end - end - - it "should return the name converted to a string when the name is not a regex" do - pending "Need to define LoadedCode behaviour first" - name = Puppet::Parser::AST::HostName.new(:value => "foo") - Puppet::Resource::Type.new(:node, name).name.should == "foo" - end - - it "should return the name converted to a string when the name is a regex" do - pending "Need to define LoadedCode behaviour first" - name = Puppet::Parser::AST::HostName.new(:value => /regex/) - Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s - end - - it "should mark any created scopes as a node scope" do - pending "Need to define LoadedCode behaviour first" - name = Puppet::Parser::AST::HostName.new(:value => /regex/) - Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s - end - end - - describe "when initializing" do - it "should require a resource super type" do - Puppet::Resource::Type.new(:hostclass, "foo").type.should == :hostclass - end - - it "should fail if provided an invalid resource super type" do - lambda { Puppet::Resource::Type.new(:nope, "foo") }.should raise_error(ArgumentError) - end - - it "should set its name to the downcased, stringified provided name" do - Puppet::Resource::Type.new(:hostclass, "Foo::Bar".intern).name.should == "foo::bar" - end - - it "should set its namespace to the downcased, stringified qualified name for classes" do - Puppet::Resource::Type.new(:hostclass, "Foo::Bar::Baz".intern).namespace.should == "foo::bar::baz" - end - - [:definition, :node].each do |type| - it "should set its namespace to the downcased, stringified qualified portion of the name for #{type}s" do - Puppet::Resource::Type.new(type, "Foo::Bar::Baz".intern).namespace.should == "foo::bar" - end - end - - %w{code line file doc}.each do |arg| - it "should set #{arg} if provided" do - type = Puppet::Resource::Type.new(:hostclass, "foo", arg.to_sym => "something") - type.send(arg).should == "something" - end - end - - it "should set any provided arguments with the keys as symbols" do - type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {:foo => "bar", :baz => "biz"}) - type.should be_valid_parameter("foo") - type.should be_valid_parameter("baz") - end - - it "should set any provided arguments with they keys as strings" do - type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar", "baz" => "biz"}) - type.should be_valid_parameter(:foo) - type.should be_valid_parameter(:baz) - end - - it "should function if provided no arguments" do - type = Puppet::Resource::Type.new(:hostclass, "foo") - type.should_not be_valid_parameter(:foo) - end - end - - describe "when testing the validity of an attribute" do - it "should return true if the parameter was typed at initialization" do - Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar"}).should be_valid_parameter("foo") - end - - it "should return true if it is a metaparam" do - Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("require") - end - - it "should return true if the parameter is named 'name'" do - Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("name") - end - - it "should return false if it is not a metaparam and was not provided at initialization" do - Puppet::Resource::Type.new(:hostclass, "foo").should_not be_valid_parameter("yayness") - end - end - - describe "when creating a subscope" do - before do - @scope = stub 'scope', :newscope => nil - @resource = stub 'resource' - @type = Puppet::Resource::Type.new(:hostclass, "foo") - end - - it "should return a new scope created with the provided scope as the parent" do - @scope.expects(:newscope).returns "foo" - @type.subscope(@scope, @resource).should == "foo" - end - - it "should set the source as itself" do - @scope.expects(:newscope).with { |args| args[:source] == @type } - @type.subscope(@scope, @resource) - end - - it "should set the scope's namespace to its namespace" do - @type.expects(:namespace).returns "yayness" - @scope.expects(:newscope).with { |args| args[:namespace] == "yayness" } - @type.subscope(@scope, @resource) - end - - it "should set the scope's resource to the provided resource" do - @scope.expects(:newscope).with { |args| args[:resource] == @resource } - @type.subscope(@scope, @resource) - end - end - - describe "when setting its parameters in the scope" do - before do - @scope = Puppet::Parser::Scope.new(:compiler => stub("compiler", :environment => Puppet::Node::Environment.new), :source => stub("source")) - @resource = Puppet::Parser::Resource.new(:foo, "bar", :scope => @scope) - @type = Puppet::Resource::Type.new(:hostclass, "foo") - end - - it "should set each of the resource's parameters as variables in the scope" do - @type.set_arguments :foo => nil, :boo => nil - @resource[:foo] = "bar" - @resource[:boo] = "baz" - - @type.set_resource_parameters(@resource, @scope) - - @scope.lookupvar("foo").should == "bar" - @scope.lookupvar("boo").should == "baz" - end - - it "should set the variables as strings" do - @type.set_arguments :foo => nil - @resource[:foo] = "bar" - - @type.set_resource_parameters(@resource, @scope) - - @scope.lookupvar("foo").should == "bar" - end - - it "should fail if any of the resource's parameters are not valid attributes" do - @type.set_arguments :foo => nil - @resource[:boo] = "baz" - - lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError) - end - - it "should evaluate and set its default values as variables for parameters not provided by the resource" do - @type.set_arguments :foo => stub("value", :safeevaluate => "something") - @type.set_resource_parameters(@resource, @scope) - @scope.lookupvar("foo").should == "something" - end - - it "should set all default values as parameters in the resource" do - @type.set_arguments :foo => stub("value", :safeevaluate => "something") - - @type.set_resource_parameters(@resource, @scope) - - @resource[:foo].should == "something" - end - - it "should fail if the resource does not provide a value for a required argument" do - @type.set_arguments :foo => nil - @resource.expects(:to_hash).returns({}) - - lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError) - end - - it "should set the resource's title as a variable if not otherwise provided" do - @type.set_resource_parameters(@resource, @scope) - - @scope.lookupvar("title").should == "bar" - end - - it "should set the resource's name as a variable if not otherwise provided" do - @type.set_resource_parameters(@resource, @scope) - - @scope.lookupvar("name").should == "bar" - end - - it "should set its module name in the scope if available" do - @type.module_name = "mymod" - - @type.set_resource_parameters(@resource, @scope) - - @scope.lookupvar("module_name").should == "mymod" - end - - it "should set its caller module name in the scope if available" do - @scope.expects(:parent_module_name).returns "mycaller" - - @type.set_resource_parameters(@resource, @scope) - - @scope.lookupvar("caller_module_name").should == "mycaller" - end - end - - describe "when describing and managing parent classes" do - before do - @code = Puppet::Resource::TypeCollection.new("env") - @parent = Puppet::Resource::Type.new(:hostclass, "bar") - @code.add @parent - - @child = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") - @code.add @child - end - - it "should be able to define a parent" do - Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") - end - - it "should use the code collection to find the parent resource type" do - @child.parent_type.should equal(@parent) - end - - it "should be able to find parent nodes" do - parent = Puppet::Resource::Type.new(:node, "bar") - @code.add parent - child = Puppet::Resource::Type.new(:node, "foo", :parent => "bar") - @code.add child - - child.parent_type.should equal(parent) - end - - it "should cache a reference to the parent type" do - @code.expects(:hostclass).once.with("bar").returns @parent - @child.parent_type - @child.parent_type - end - - it "should correctly state when it is another type's child" do - @child.should be_child_of(@parent) - end - - it "should be considered the child of a parent's parent" do - @grandchild = Puppet::Resource::Type.new(:hostclass, "baz", :parent => "foo") - @code.add @grandchild - - @grandchild.should be_child_of(@parent) - end - - it "should correctly state when it is not another type's child" do - @notchild = Puppet::Resource::Type.new(:hostclass, "baz") - @code.add @notchild - - @notchild.should_not be_child_of(@parent) - end - end - - describe "when evaluating its code" do - before do - @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) - @scope = Puppet::Parser::Scope.new :compiler => @compiler - @resource = Puppet::Parser::Resource.new(:foo, "yay", :scope => @scope) - - # This is so the internal resource lookup works, yo. - @compiler.catalog.add_resource @resource - - @known_resource_types = stub 'known_resource_types' - @resource.stubs(:known_resource_types).returns @known_resource_types - @type = Puppet::Resource::Type.new(:hostclass, "foo") - end - - it "should set all of its parameters in a subscope" do - subscope = stub 'subscope', :compiler => @compiler - @type.expects(:subscope).with(@scope, @resource).returns subscope - @type.expects(:set_resource_parameters).with(@resource, subscope) - - @type.evaluate_code(@resource) - end - - it "should not create a subscope for the :main class" do - @resource.stubs(:title).returns(:main) - @type.expects(:subscope).never - @type.expects(:set_resource_parameters).with(@resource, @scope) - - @type.evaluate_code(@resource) - end - - it "should store the class scope" do - @type.evaluate_code(@resource) - @scope.class_scope(@type).should be_instance_of(@scope.class) - end - - it "should still create a scope but not store it if the type is a definition" do - @type = Puppet::Resource::Type.new(:definition, "foo") - @type.evaluate_code(@resource) - @scope.class_scope(@type).should be_nil - end - - it "should evaluate the AST code if any is provided" do - code = stub 'code' - @type.stubs(:code).returns code - @type.stubs(:subscope).returns stub_everything("subscope", :compiler => @compiler) - code.expects(:safeevaluate).with @type.subscope - - @type.evaluate_code(@resource) - end - - describe "and ruby code is provided" do - it "should create a DSL Resource API and evaluate it" do - @type.stubs(:ruby_code).returns(proc { "foo" }) - @api = stub 'api' - Puppet::DSL::ResourceAPI.expects(:new).with { |res, scope, code| code == @type.ruby_code }.returns @api - @api.expects(:evaluate) - - @type.evaluate_code(@resource) - end - end - - it "should noop if there is no code" do - @type.expects(:code).returns nil - - @type.evaluate_code(@resource) - end - - describe "and it has a parent class" do - before do - @parent_type = Puppet::Resource::Type.new(:hostclass, "parent") - @type.parent = "parent" - @parent_resource = Puppet::Parser::Resource.new(:class, "parent", :scope => @scope) - - @compiler.add_resource @scope, @parent_resource - - @type.resource_type_collection = @scope.known_resource_types - @type.resource_type_collection.add @parent_type - end - - it "should evaluate the parent's resource" do - @type.evaluate_code(@resource) - - @scope.class_scope(@parent_type).should_not be_nil - end - - it "should not evaluate the parent's resource if it has already been evaluated" do - @parent_resource.evaluate - - @parent_resource.expects(:evaluate).never - - @type.evaluate_code(@resource) - end - - it "should use the parent's scope as its base scope" do - @type.evaluate_code(@resource) - - @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id - end - end - - describe "and it has a parent node" do - before do - @type = Puppet::Resource::Type.new(:node, "foo") - @parent_type = Puppet::Resource::Type.new(:node, "parent") - @type.parent = "parent" - @parent_resource = Puppet::Parser::Resource.new(:node, "parent", :scope => @scope) - - @compiler.add_resource @scope, @parent_resource - - @type.resource_type_collection = @scope.known_resource_types - @type.resource_type_collection.stubs(:node).with("parent").returns(@parent_type) - @type.resource_type_collection.stubs(:node).with("Parent").returns(@parent_type) - end - - it "should evaluate the parent's resource" do - @type.evaluate_code(@resource) - - @scope.class_scope(@parent_type).should_not be_nil - end - - it "should not evaluate the parent's resource if it has already been evaluated" do - @parent_resource.evaluate - - @parent_resource.expects(:evaluate).never - - @type.evaluate_code(@resource) - end - - it "should use the parent's scope as its base scope" do - @type.evaluate_code(@resource) - - @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id - end - end - end - - describe "when creating a resource" do - before do - @node = Puppet::Node.new("foo") - @compiler = Puppet::Parser::Compiler.new(@node) - @scope = Puppet::Parser::Scope.new(:compiler => @compiler) - - @top = Puppet::Resource::Type.new :hostclass, "top" - @middle = Puppet::Resource::Type.new :hostclass, "middle", :parent => "top" - - @code = Puppet::Resource::TypeCollection.new("env") - @code.add @top - @code.add @middle - end - - it "should create a resource instance" do - @top.mk_plain_resource(@scope).should be_instance_of(Puppet::Parser::Resource) - end - - it "should set its resource type to 'class' when it is a hostclass" do - Puppet::Resource::Type.new(:hostclass, "top").mk_plain_resource(@scope).type.should == "Class" - end - - it "should set its resource type to 'node' when it is a node" do - Puppet::Resource::Type.new(:node, "top").mk_plain_resource(@scope).type.should == "Node" - end - - it "should fail when it is a definition" do - lambda { Puppet::Resource::Type.new(:definition, "top").mk_plain_resource(@scope) }.should raise_error(ArgumentError) - end - - it "should add the created resource to the scope's catalog" do - @top.mk_plain_resource(@scope) - - @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) - end - - it "should evaluate the parent class if one exists" do - @middle.mk_plain_resource(@scope) - - @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) - end - - it "should fail to evaluate if a parent class is defined but cannot be found" do - othertop = Puppet::Resource::Type.new :hostclass, "something", :parent => "yay" - @code.add othertop - lambda { othertop.mk_plain_resource(@scope) }.should raise_error(Puppet::ParseError) - end - - it "should not create a new resource if one already exists" do - @compiler.catalog.expects(:resource).with(:class, "top").returns("something") - @compiler.catalog.expects(:add_resource).never - @top.mk_plain_resource(@scope) - end - - it "should return the existing resource when not creating a new one" do - @compiler.catalog.expects(:resource).with(:class, "top").returns("something") - @compiler.catalog.expects(:add_resource).never - @top.mk_plain_resource(@scope).should == "something" - end - - it "should not create a new parent resource if one already exists and it has a parent class" do - @top.mk_plain_resource(@scope) - - top_resource = @compiler.catalog.resource(:class, "top") - - @middle.mk_plain_resource(@scope) - - @compiler.catalog.resource(:class, "top").should equal(top_resource) - end - - # #795 - tag before evaluation. - it "should tag the catalog with the resource tags when it is evaluated" do - @middle.mk_plain_resource(@scope) - - @compiler.catalog.should be_tagged("middle") - end - - it "should tag the catalog with the parent class tags when it is evaluated" do - @middle.mk_plain_resource(@scope) - - @compiler.catalog.should be_tagged("top") - end - end - - describe "when merging code from another instance" do - def code(str) - Puppet::Parser::AST::Leaf.new :value => str - end - - it "should fail unless it is a class" do - lambda { Puppet::Resource::Type.new(:node, "bar").merge("foo") }.should raise_error(Puppet::Error) - end - - it "should fail unless the source instance is a class" do - dest = Puppet::Resource::Type.new(:hostclass, "bar") - source = Puppet::Resource::Type.new(:node, "foo") - lambda { dest.merge(source) }.should raise_error(Puppet::Error) - end - - it "should fail if both classes have different parent classes" do - code = Puppet::Resource::TypeCollection.new("env") - {"a" => "b", "c" => "d"}.each do |parent, child| - code.add Puppet::Resource::Type.new(:hostclass, parent) - code.add Puppet::Resource::Type.new(:hostclass, child, :parent => parent) - end - lambda { code.hostclass("b").merge(code.hostclass("d")) }.should raise_error(Puppet::Error) - end - - it "should fail if it's named 'main' and 'freeze_main' is enabled" do - Puppet.settings[:freeze_main] = true - code = Puppet::Resource::TypeCollection.new("env") - code.add Puppet::Resource::Type.new(:hostclass, "") - other = Puppet::Resource::Type.new(:hostclass, "") - lambda { code.hostclass("").merge(other) }.should raise_error(Puppet::Error) - end - - it "should copy the other class's parent if it has not parent" do - dest = Puppet::Resource::Type.new(:hostclass, "bar") - - parent = Puppet::Resource::Type.new(:hostclass, "parent") - source = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "parent") - dest.merge(source) - - dest.parent.should == "parent" - end - - it "should copy the other class's documentation as its docs if it has no docs" do - dest = Puppet::Resource::Type.new(:hostclass, "bar") - source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness") - dest.merge(source) - - dest.doc.should == "yayness" - end - - it "should append the other class's docs to its docs if it has any" do - dest = Puppet::Resource::Type.new(:hostclass, "bar", :doc => "fooness") - source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness") - dest.merge(source) - - dest.doc.should == "foonessyayness" - end - - it "should turn its code into an ASTArray if necessary" do - dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => code("foo")) - source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar")) - - dest.merge(source) - - dest.code.should be_instance_of(Puppet::Parser::AST::ASTArray) - end - - it "should set the other class's code as its code if it has none" do - dest = Puppet::Resource::Type.new(:hostclass, "bar") - source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar")) - - dest.merge(source) - - dest.code.value.should == "bar" - end - - it "should append the other class's code to its code if it has any" do - dcode = Puppet::Parser::AST::ASTArray.new :children => [code("dest")] - dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => dcode) - - scode = Puppet::Parser::AST::ASTArray.new :children => [code("source")] - source = Puppet::Resource::Type.new(:hostclass, "foo", :code => scode) - - dest.merge(source) - - dest.code.children.collect { |l| l.value }.should == %w{dest source} - end - end -end diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb new file mode 100755 index 000000000..712bc2c8a --- /dev/null +++ b/spec/unit/resource_spec.rb @@ -0,0 +1,805 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' +require 'puppet/resource' + +describe Puppet::Resource do + + before do + @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" + end + + [:catalog, :file, :line].each do |attr| + it "should have an #{attr} attribute" do + resource = Puppet::Resource.new("file", "/my/file") + resource.should respond_to(attr) + resource.should respond_to(attr.to_s + "=") + end + end + + it "should have a :title attribute" do + Puppet::Resource.new(:user, "foo").title.should == "foo" + end + + it "should require the type and title" do + lambda { Puppet::Resource.new }.should raise_error(ArgumentError) + end + + it "should canonize types to capitalized strings" do + Puppet::Resource.new(:user, "foo").type.should == "User" + end + + it "should canonize qualified types so all strings are capitalized" do + Puppet::Resource.new("foo::bar", "foo").type.should == "Foo::Bar" + end + + it "should tag itself with its type" do + Puppet::Resource.new("file", "/f").should be_tagged("file") + end + + it "should tag itself with its title if the title is a valid tag" do + Puppet::Resource.new("user", "bar").should be_tagged("bar") + end + + it "should not tag itself with its title if the title is a not valid tag" do + Puppet::Resource.new("file", "/bar").should_not be_tagged("/bar") + end + + it "should allow setting of attributes" do + Puppet::Resource.new("file", "/bar", :file => "/foo").file.should == "/foo" + Puppet::Resource.new("file", "/bar", :exported => true).should be_exported + end + + it "should set its type to 'Class' and its title to the passed title if the passed type is :component and the title has no square brackets in it" do + ref = Puppet::Resource.new(:component, "foo") + ref.type.should == "Class" + ref.title.should == "Foo" + end + + it "should interpret the title as a reference and assign appropriately if the type is :component and the title contains square brackets" do + ref = Puppet::Resource.new(:component, "foo::bar[yay]") + ref.type.should == "Foo::Bar" + ref.title.should == "yay" + end + + it "should set the type to 'Class' if it is nil and the title contains no square brackets" do + ref = Puppet::Resource.new(nil, "yay") + ref.type.should == "Class" + ref.title.should == "Yay" + end + + it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains square brackets" do + ref = Puppet::Resource.new(nil, "foo::bar[yay]") + ref.type.should == "Foo::Bar" + ref.title.should == "yay" + end + + it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains nested square brackets" do + ref = Puppet::Resource.new(nil, "foo::bar[baz[yay]]") + ref.type.should == "Foo::Bar" + ref.title.should =="baz[yay]" + end + + it "should interpret the type as a reference and assign appropriately if the title is nil and the type contains square brackets" do + ref = Puppet::Resource.new("foo::bar[baz]") + ref.type.should == "Foo::Bar" + ref.title.should =="baz" + end + + it "should be able to extract its information from a Puppet::Type instance" do + ral = Puppet::Type.type(:file).new :path => @basepath+"/foo" + ref = Puppet::Resource.new(ral) + ref.type.should == "File" + ref.title.should == @basepath+"/foo" + end + + + it "should fail if the title is nil and the type is not a valid resource reference string" do + lambda { Puppet::Resource.new("foo") }.should raise_error(ArgumentError) + end + + it "should be able to produce a backward-compatible reference array" do + Puppet::Resource.new("foobar", "/f").to_trans_ref.should == %w{Foobar /f} + end + + it "should be taggable" do + Puppet::Resource.ancestors.should be_include(Puppet::Util::Tagging) + end + + it "should have an 'exported' attribute" do + resource = Puppet::Resource.new("file", "/f") + resource.exported = true + resource.exported.should == true + resource.should be_exported + end + + it "should support an environment attribute" do + Puppet::Resource.new("file", "/my/file", :environment => :foo).environment.name.should == :foo + end + + it "should support specifying namespaces" do + Puppet::Resource.new("file", "/my/file", :namespaces => ["foo"]).namespaces.should == ["foo"] + end + + it "should convert namespaces to an array if not specified as one" do + Puppet::Resource.new("file", "/my/file", :namespaces => "foo").namespaces.should == ["foo"] + end + + it "should default to a single amespace of an empty string" do + Puppet::Resource.new("file", "/my/file").namespaces.should == [""] + end + + describe "and munging its type and title" do + describe "when modeling a builtin resource" do + it "should be able to find the resource type" do + Puppet::Resource.new("file", "/my/file").resource_type.should equal(Puppet::Type.type(:file)) + end + + it "should set its type to the capitalized type name" do + Puppet::Resource.new("file", "/my/file").type.should == "File" + end + end + + describe "when modeling a defined resource" do + describe "that exists" do + before do + @type = Puppet::Resource::Type.new(:definition, "foo::bar") + Puppet::Node::Environment.new.known_resource_types.add @type + end + + it "should set its type to the capitalized type name" do + Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" + end + + it "should be able to find the resource type" do + Puppet::Resource.new("foo::bar", "/my/file").resource_type.should equal(@type) + end + + it "should set its title to the provided title" do + Puppet::Resource.new("foo::bar", "/my/file").title.should == "/my/file" + end + + describe "and the resource is unqualified and models a qualified resource type" do + it "should set its type to the fully qualified resource type" do + Puppet::Resource.new("bar", "/my/file", :namespaces => %w{foo}).type.should == "Foo::Bar" + end + + it "should be able to find the resource type" do + Puppet::Resource.new("bar", "/my/file", :namespaces => %w{foo}).resource_type.should equal(@type) + end + end + end + + describe "that does not exist" do + it "should set its resource type to the capitalized resource type name" do + Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" + end + end + end + + describe "when modeling a node" do + # Life's easier with nodes, because they can't be qualified. + it "should set its type to 'Node' and its title to the provided title" do + node = Puppet::Resource.new("node", "foo") + node.type.should == "Node" + node.title.should == "foo" + end + end + + describe "when modeling a class" do + it "should set its type to 'Class'" do + Puppet::Resource.new("class", "foo").type.should == "Class" + end + + describe "that exists" do + before do + @type = Puppet::Resource::Type.new(:hostclass, "foo::bar") + Puppet::Node::Environment.new.known_resource_types.add @type + end + + it "should set its title to the capitalized, fully qualified resource type" do + Puppet::Resource.new("class", "foo::bar").title.should == "Foo::Bar" + end + + it "should be able to find the resource type" do + Puppet::Resource.new("class", "foo::bar").resource_type.should equal(@type) + end + + describe "and the resource is unqualified and models a qualified class" do + it "should set its title to the fully qualified resource type" do + Puppet::Resource.new("class", "bar", :namespaces => %w{foo}).title.should == "Foo::Bar" + end + + it "should be able to find the resource type" do + Puppet::Resource.new("class", "bar", :namespaces => %w{foo}).resource_type.should equal(@type) + end + + it "should set its type to 'Class'" do + Puppet::Resource.new("class", "bar", :namespaces => %w{foo}).type.should == "Class" + end + end + end + + describe "that does not exist" do + it "should set its type to 'Class' and its title to the capitalized provided name" do + klass = Puppet::Resource.new("class", "foo::bar") + klass.type.should == "Class" + klass.title.should == "Foo::Bar" + end + end + + describe "and its name is set to the empty string" do + it "should set its title to :main" do + Puppet::Resource.new("class", "").title.should == :main + end + + describe "and a class exists whose name is the empty string" do # this was a bit tough to track down + it "should set its title to :main" do + @type = Puppet::Resource::Type.new(:hostclass, "") + Puppet::Node::Environment.new.known_resource_types.add @type + + Puppet::Resource.new("class", "").title.should == :main + end + end + end + + describe "and its name is set to :main" do + it "should set its title to :main" do + Puppet::Resource.new("class", :main).title.should == :main + end + + describe "and a class exists whose name is the empty string" do # this was a bit tough to track down + it "should set its title to :main" do + @type = Puppet::Resource::Type.new(:hostclass, "") + Puppet::Node::Environment.new.known_resource_types.add @type + + Puppet::Resource.new("class", :main).title.should == :main + end + end + end + end + end + + it "should return nil when looking up resource types that don't exist" do + Puppet::Resource.new("foobar", "bar").resource_type.should be_nil + end + + it "should not fail when an invalid parameter is used and strict mode is disabled" do + type = Puppet::Resource::Type.new(:definition, "foobar") + Puppet::Node::Environment.new.known_resource_types.add type + resource = Puppet::Resource.new("foobar", "/my/file") + resource[:yay] = true + end + + it "should be considered equivalent to another resource if their type and title match and no parameters are set" do + Puppet::Resource.new("file", "/f").should == Puppet::Resource.new("file", "/f") + end + + it "should be considered equivalent to another resource if their type, title, and parameters are equal" do + Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}).should == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) + end + + it "should not be considered equivalent to another resource if their type and title match but parameters are different" do + Puppet::Resource.new("file", "/f", :parameters => {:fee => "baz"}).should_not == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) + end + + it "should not be considered equivalent to a non-resource" do + Puppet::Resource.new("file", "/f").should_not == "foo" + end + + it "should not be considered equivalent to another resource if their types do not match" do + Puppet::Resource.new("file", "/f").should_not == Puppet::Resource.new("exec", "/f") + end + + it "should not be considered equivalent to another resource if their titles do not match" do + Puppet::Resource.new("file", "/foo").should_not == Puppet::Resource.new("file", "/f") + end + + describe "when referring to a resource with name canonicalization" do + it "should canonicalize its own name" do + res = Puppet::Resource.new("file", "/path/") + res.uniqueness_key.should == ["/path"] + res.ref.should == "File[/path/]" + end + end + + describe "when running in strict mode" do + it "should be strict" do + Puppet::Resource.new("file", "/path", :strict => true).should be_strict + end + + it "should fail if invalid parameters are used" do + lambda { Puppet::Resource.new("file", "/path", :strict => true, :parameters => {:nosuchparam => "bar"}) }.should raise_error + end + + it "should fail if the resource type cannot be resolved" do + lambda { Puppet::Resource.new("nosuchtype", "/path", :strict => true) }.should raise_error + end + end + + describe "when managing parameters" do + before do + @resource = Puppet::Resource.new("file", "/my/file") + end + + it "should correctly detect when provided parameters are not valid for builtin types" do + Puppet::Resource.new("file", "/my/file").should_not be_valid_parameter("foobar") + end + + it "should correctly detect when provided parameters are valid for builtin types" do + Puppet::Resource.new("file", "/my/file").should be_valid_parameter("mode") + end + + it "should correctly detect when provided parameters are not valid for defined resource types" do + type = Puppet::Resource::Type.new(:definition, "foobar") + Puppet::Node::Environment.new.known_resource_types.add type + Puppet::Resource.new("foobar", "/my/file").should_not be_valid_parameter("myparam") + end + + it "should correctly detect when provided parameters are valid for defined resource types" do + type = Puppet::Resource::Type.new(:definition, "foobar", :arguments => {"myparam" => nil}) + Puppet::Node::Environment.new.known_resource_types.add type + Puppet::Resource.new("foobar", "/my/file").should be_valid_parameter("myparam") + end + + it "should allow setting and retrieving of parameters" do + @resource[:foo] = "bar" + @resource[:foo].should == "bar" + end + + it "should allow setting of parameters at initialization" do + Puppet::Resource.new("file", "/my/file", :parameters => {:foo => "bar"})[:foo].should == "bar" + end + + it "should canonicalize retrieved parameter names to treat symbols and strings equivalently" do + @resource[:foo] = "bar" + @resource["foo"].should == "bar" + end + + it "should canonicalize set parameter names to treat symbols and strings equivalently" do + @resource["foo"] = "bar" + @resource[:foo].should == "bar" + end + + it "should set the namevar when asked to set the name" do + resource = Puppet::Resource.new("user", "bob") + Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] + resource[:name] = "bob" + resource[:myvar].should == "bob" + end + + it "should return the namevar when asked to return the name" do + resource = Puppet::Resource.new("user", "bob") + Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] + resource[:myvar] = "test" + resource[:name].should == "test" + end + + it "should be able to set the name for non-builtin types" do + resource = Puppet::Resource.new(:foo, "bar") + resource[:name] = "eh" + lambda { resource[:name] = "eh" }.should_not raise_error + end + + it "should be able to return the name for non-builtin types" do + resource = Puppet::Resource.new(:foo, "bar") + resource[:name] = "eh" + resource[:name].should == "eh" + end + + it "should be able to iterate over parameters" do + @resource[:foo] = "bar" + @resource[:fee] = "bare" + params = {} + @resource.each do |key, value| + params[key] = value + end + params.should == {:foo => "bar", :fee => "bare"} + end + + it "should include Enumerable" do + @resource.class.ancestors.should be_include(Enumerable) + end + + it "should have a method for testing whether a parameter is included" do + @resource[:foo] = "bar" + @resource.should be_has_key(:foo) + @resource.should_not be_has_key(:eh) + end + + it "should have a method for providing the list of parameters" do + @resource[:foo] = "bar" + @resource[:bar] = "foo" + keys = @resource.keys + keys.should be_include(:foo) + keys.should be_include(:bar) + end + + it "should have a method for providing the number of parameters" do + @resource[:foo] = "bar" + @resource.length.should == 1 + end + + it "should have a method for deleting parameters" do + @resource[:foo] = "bar" + @resource.delete(:foo) + @resource[:foo].should be_nil + end + + it "should have a method for testing whether the parameter list is empty" do + @resource.should be_empty + @resource[:foo] = "bar" + @resource.should_not be_empty + end + + it "should be able to produce a hash of all existing parameters" do + @resource[:foo] = "bar" + @resource[:fee] = "yay" + + hash = @resource.to_hash + hash[:foo].should == "bar" + hash[:fee].should == "yay" + end + + it "should not provide direct access to the internal parameters hash when producing a hash" do + hash = @resource.to_hash + hash[:foo] = "bar" + @resource[:foo].should be_nil + end + + it "should use the title as the namevar to the hash if no namevar is present" do + resource = Puppet::Resource.new("user", "bob") + Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] + resource.to_hash[:myvar].should == "bob" + end + + it "should set :name to the title if :name is not present for non-builtin types" do + krt = Puppet::Resource::TypeCollection.new("myenv") + krt.add Puppet::Resource::Type.new(:definition, :foo) + resource = Puppet::Resource.new :foo, "bar" + resource.stubs(:known_resource_types).returns krt + resource.to_hash[:name].should == "bar" + end + end + + describe "when serializing" do + before do + @resource = Puppet::Resource.new("file", "/my/file") + @resource["one"] = "test" + @resource["two"] = "other" + end + + it "should be able to be dumped to yaml" do + proc { YAML.dump(@resource) }.should_not raise_error + end + + it "should produce an equivalent yaml object" do + text = YAML.dump(@resource) + + newresource = YAML.load(text) + newresource.title.should == @resource.title + newresource.type.should == @resource.type + %w{one two}.each do |param| + newresource[param].should == @resource[param] + end + end + end + + describe "when converting to a RAL resource" do + it "should use the resource type's :new method to create the resource if the resource is of a builtin type" do + resource = Puppet::Resource.new("file", @basepath+"/my/file") + result = resource.to_ral + result.should be_instance_of(Puppet::Type.type(:file)) + result[:path].should == @basepath+"/my/file" + end + + it "should convert to a component instance if the resource type is not of a builtin type" do + resource = Puppet::Resource.new("foobar", "somename") + result = resource.to_ral + + result.should be_instance_of(Puppet::Type.type(:component)) + result.title.should == "Foobar[somename]" + end + end + + it "should be able to convert itself to Puppet code" do + Puppet::Resource.new("one::two", "/my/file").should respond_to(:to_manifest) + end + + describe "when converting to puppet code" do + before do + @resource = Puppet::Resource.new("one::two", "/my/file", :parameters => {:noop => true, :foo => %w{one two}}) + end + + it "should print the type and title" do + @resource.to_manifest.should be_include("one::two { '/my/file':\n") + end + + it "should print each parameter, with the value single-quoted" do + @resource.to_manifest.should be_include(" noop => 'true'") + end + + it "should print array values appropriately" do + @resource.to_manifest.should be_include(" foo => ['one','two']") + end + end + + it "should be able to convert itself to a TransObject instance" do + Puppet::Resource.new("one::two", "/my/file").should respond_to(:to_trans) + end + + describe "when converting to a TransObject" do + describe "and the resource is not an instance of a builtin type" do + before do + @resource = Puppet::Resource.new("foo", "bar") + end + + it "should return a simple TransBucket if it is not an instance of a builtin type" do + bucket = @resource.to_trans + bucket.should be_instance_of(Puppet::TransBucket) + bucket.type.should == @resource.type + bucket.name.should == @resource.title + end + + it "should return a simple TransBucket if it is a stage" do + @resource = Puppet::Resource.new("stage", "bar") + bucket = @resource.to_trans + bucket.should be_instance_of(Puppet::TransBucket) + bucket.type.should == @resource.type + bucket.name.should == @resource.title + end + + it "should copy over the resource's file" do + @resource.file = "/foo/bar" + @resource.to_trans.file.should == "/foo/bar" + end + + it "should copy over the resource's line" do + @resource.line = 50 + @resource.to_trans.line.should == 50 + end + end + + describe "and the resource is an instance of a builtin type" do + before do + @resource = Puppet::Resource.new("file", "bar") + end + + it "should return a TransObject if it is an instance of a builtin resource type" do + trans = @resource.to_trans + trans.should be_instance_of(Puppet::TransObject) + trans.type.should == "file" + trans.name.should == @resource.title + end + + it "should copy over the resource's file" do + @resource.file = "/foo/bar" + @resource.to_trans.file.should == "/foo/bar" + end + + it "should copy over the resource's line" do + @resource.line = 50 + @resource.to_trans.line.should == 50 + end + + # Only TransObjects support tags, annoyingly + it "should copy over the resource's tags" do + @resource.tag "foo" + @resource.to_trans.tags.should == @resource.tags + end + + it "should copy the resource's parameters into the transobject and convert the parameter name to a string" do + @resource[:foo] = "bar" + @resource.to_trans["foo"].should == "bar" + end + + it "should be able to copy arrays of values" do + @resource[:foo] = %w{yay fee} + @resource.to_trans["foo"].should == %w{yay fee} + end + + it "should reduce single-value arrays to just a value" do + @resource[:foo] = %w{yay} + @resource.to_trans["foo"].should == "yay" + end + + it "should convert resource references into the backward-compatible form" do + @resource[:foo] = Puppet::Resource.new(:file, "/f") + @resource.to_trans["foo"].should == %w{File /f} + end + + it "should convert resource references into the backward-compatible form even when within arrays" do + @resource[:foo] = ["a", Puppet::Resource.new(:file, "/f")] + @resource.to_trans["foo"].should == ["a", %w{File /f}] + end + end + end + + describe "when converting to pson" do + confine "Missing 'pson' library" => Puppet.features.pson? + + def pson_output_should + @resource.class.expects(:pson_create).with { |hash| yield hash } + end + + it "should include the pson util module" do + Puppet::Resource.singleton_class.ancestors.should be_include(Puppet::Util::Pson) + end + + # LAK:NOTE For all of these tests, we convert back to the resource so we can + # trap the actual data structure then. + + it "should set its type to the provided type" do + Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).type.should == "File" + end + + it "should set its title to the provided title" do + Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).title.should == "/foo" + end + + it "should include all tags from the resource" do + resource = Puppet::Resource.new("File", "/foo") + resource.tag("yay") + + Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).tags.should == resource.tags + end + + it "should include the file if one is set" do + resource = Puppet::Resource.new("File", "/foo") + resource.file = "/my/file" + + Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).file.should == "/my/file" + end + + it "should include the line if one is set" do + resource = Puppet::Resource.new("File", "/foo") + resource.line = 50 + + Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).line.should == 50 + end + + it "should include the 'exported' value if one is set" do + resource = Puppet::Resource.new("File", "/foo") + resource.exported = true + + Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported.should be_true + end + + it "should set 'exported' to false if no value is set" do + resource = Puppet::Resource.new("File", "/foo") + + Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported.should be_false + end + + it "should set all of its parameters as the 'parameters' entry" do + resource = Puppet::Resource.new("File", "/foo") + resource[:foo] = %w{bar eh} + resource[:fee] = %w{baz} + + result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) + result["foo"].should == %w{bar eh} + result["fee"].should == %w{baz} + end + + it "should serialize relationships as reference strings" do + resource = Puppet::Resource.new("File", "/foo") + resource[:requires] = Puppet::Resource.new("File", "/bar") + result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) + result[:requires].should == "File[/bar]" + end + end + + describe "when converting from pson" do + confine "Missing 'pson' library" => Puppet.features.pson? + + def pson_result_should + Puppet::Resource.expects(:new).with { |hash| yield hash } + end + + before do + @data = { + 'type' => "file", + 'title' => @basepath+"/yay", + } + end + + it "should set its type to the provided type" do + Puppet::Resource.from_pson(@data).type.should == "File" + end + + it "should set its title to the provided title" do + Puppet::Resource.from_pson(@data).title.should == @basepath+"/yay" + end + + it "should tag the resource with any provided tags" do + @data['tags'] = %w{foo bar} + resource = Puppet::Resource.from_pson(@data) + resource.tags.should be_include("foo") + resource.tags.should be_include("bar") + end + + it "should set its file to the provided file" do + @data['file'] = "/foo/bar" + Puppet::Resource.from_pson(@data).file.should == "/foo/bar" + end + + it "should set its line to the provided line" do + @data['line'] = 50 + Puppet::Resource.from_pson(@data).line.should == 50 + end + + it "should 'exported' to true if set in the pson data" do + @data['exported'] = true + Puppet::Resource.from_pson(@data).exported.should be_true + end + + it "should 'exported' to false if not set in the pson data" do + Puppet::Resource.from_pson(@data).exported.should be_false + end + + it "should fail if no title is provided" do + @data.delete('title') + lambda { Puppet::Resource.from_pson(@data) }.should raise_error(ArgumentError) + end + + it "should fail if no type is provided" do + @data.delete('type') + lambda { Puppet::Resource.from_pson(@data) }.should raise_error(ArgumentError) + end + + it "should set each of the provided parameters" do + @data['parameters'] = {'foo' => %w{one two}, 'fee' => %w{three four}} + resource = Puppet::Resource.from_pson(@data) + resource['foo'].should == %w{one two} + resource['fee'].should == %w{three four} + end + + it "should convert single-value array parameters to normal values" do + @data['parameters'] = {'foo' => %w{one}} + resource = Puppet::Resource.from_pson(@data) + resource['foo'].should == %w{one} + end + end + + describe "it should implement to_resource" do + resource = Puppet::Resource.new("file", "/my/file") + resource.to_resource.should == resource + end + + describe "because it is an indirector model" do + it "should include Puppet::Indirector" do + Puppet::Resource.should be_is_a(Puppet::Indirector) + end + + it "should have a default terminus" do + Puppet::Resource.indirection.terminus_class.should == :ral + end + + it "should have a name" do + Puppet::Resource.new("file", "/my/file").name.should == "File//my/file" + end + end + + describe "when resolving resources with a catalog" do + it "should resolve all resources using the catalog" do + catalog = mock 'catalog' + resource = Puppet::Resource.new("foo::bar", "yay") + resource.catalog = catalog + + catalog.expects(:resource).with("Foo::Bar[yay]").returns(:myresource) + + resource.resolve.should == :myresource + end + end + + describe "when generating the uniqueness key" do + it "should include all of the key_attributes in alphabetical order by attribute name" do + Puppet::Type.type(:file).stubs(:key_attributes).returns [:myvar, :owner, :path] + Puppet::Type.type(:file).stubs(:title_patterns).returns( + [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ] + ) + res = Puppet::Resource.new("file", "/my/file", :parameters => {:owner => 'root', :content => 'hello'}) + res.uniqueness_key.should == [ nil, 'root', '/my/file'] + end + end +end diff --git a/spec/unit/resource_spec_spec.rb b/spec/unit/resource_spec_spec.rb deleted file mode 100755 index 712bc2c8a..000000000 --- a/spec/unit/resource_spec_spec.rb +++ /dev/null @@ -1,805 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' -require 'puppet/resource' - -describe Puppet::Resource do - - before do - @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" - end - - [:catalog, :file, :line].each do |attr| - it "should have an #{attr} attribute" do - resource = Puppet::Resource.new("file", "/my/file") - resource.should respond_to(attr) - resource.should respond_to(attr.to_s + "=") - end - end - - it "should have a :title attribute" do - Puppet::Resource.new(:user, "foo").title.should == "foo" - end - - it "should require the type and title" do - lambda { Puppet::Resource.new }.should raise_error(ArgumentError) - end - - it "should canonize types to capitalized strings" do - Puppet::Resource.new(:user, "foo").type.should == "User" - end - - it "should canonize qualified types so all strings are capitalized" do - Puppet::Resource.new("foo::bar", "foo").type.should == "Foo::Bar" - end - - it "should tag itself with its type" do - Puppet::Resource.new("file", "/f").should be_tagged("file") - end - - it "should tag itself with its title if the title is a valid tag" do - Puppet::Resource.new("user", "bar").should be_tagged("bar") - end - - it "should not tag itself with its title if the title is a not valid tag" do - Puppet::Resource.new("file", "/bar").should_not be_tagged("/bar") - end - - it "should allow setting of attributes" do - Puppet::Resource.new("file", "/bar", :file => "/foo").file.should == "/foo" - Puppet::Resource.new("file", "/bar", :exported => true).should be_exported - end - - it "should set its type to 'Class' and its title to the passed title if the passed type is :component and the title has no square brackets in it" do - ref = Puppet::Resource.new(:component, "foo") - ref.type.should == "Class" - ref.title.should == "Foo" - end - - it "should interpret the title as a reference and assign appropriately if the type is :component and the title contains square brackets" do - ref = Puppet::Resource.new(:component, "foo::bar[yay]") - ref.type.should == "Foo::Bar" - ref.title.should == "yay" - end - - it "should set the type to 'Class' if it is nil and the title contains no square brackets" do - ref = Puppet::Resource.new(nil, "yay") - ref.type.should == "Class" - ref.title.should == "Yay" - end - - it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains square brackets" do - ref = Puppet::Resource.new(nil, "foo::bar[yay]") - ref.type.should == "Foo::Bar" - ref.title.should == "yay" - end - - it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains nested square brackets" do - ref = Puppet::Resource.new(nil, "foo::bar[baz[yay]]") - ref.type.should == "Foo::Bar" - ref.title.should =="baz[yay]" - end - - it "should interpret the type as a reference and assign appropriately if the title is nil and the type contains square brackets" do - ref = Puppet::Resource.new("foo::bar[baz]") - ref.type.should == "Foo::Bar" - ref.title.should =="baz" - end - - it "should be able to extract its information from a Puppet::Type instance" do - ral = Puppet::Type.type(:file).new :path => @basepath+"/foo" - ref = Puppet::Resource.new(ral) - ref.type.should == "File" - ref.title.should == @basepath+"/foo" - end - - - it "should fail if the title is nil and the type is not a valid resource reference string" do - lambda { Puppet::Resource.new("foo") }.should raise_error(ArgumentError) - end - - it "should be able to produce a backward-compatible reference array" do - Puppet::Resource.new("foobar", "/f").to_trans_ref.should == %w{Foobar /f} - end - - it "should be taggable" do - Puppet::Resource.ancestors.should be_include(Puppet::Util::Tagging) - end - - it "should have an 'exported' attribute" do - resource = Puppet::Resource.new("file", "/f") - resource.exported = true - resource.exported.should == true - resource.should be_exported - end - - it "should support an environment attribute" do - Puppet::Resource.new("file", "/my/file", :environment => :foo).environment.name.should == :foo - end - - it "should support specifying namespaces" do - Puppet::Resource.new("file", "/my/file", :namespaces => ["foo"]).namespaces.should == ["foo"] - end - - it "should convert namespaces to an array if not specified as one" do - Puppet::Resource.new("file", "/my/file", :namespaces => "foo").namespaces.should == ["foo"] - end - - it "should default to a single amespace of an empty string" do - Puppet::Resource.new("file", "/my/file").namespaces.should == [""] - end - - describe "and munging its type and title" do - describe "when modeling a builtin resource" do - it "should be able to find the resource type" do - Puppet::Resource.new("file", "/my/file").resource_type.should equal(Puppet::Type.type(:file)) - end - - it "should set its type to the capitalized type name" do - Puppet::Resource.new("file", "/my/file").type.should == "File" - end - end - - describe "when modeling a defined resource" do - describe "that exists" do - before do - @type = Puppet::Resource::Type.new(:definition, "foo::bar") - Puppet::Node::Environment.new.known_resource_types.add @type - end - - it "should set its type to the capitalized type name" do - Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" - end - - it "should be able to find the resource type" do - Puppet::Resource.new("foo::bar", "/my/file").resource_type.should equal(@type) - end - - it "should set its title to the provided title" do - Puppet::Resource.new("foo::bar", "/my/file").title.should == "/my/file" - end - - describe "and the resource is unqualified and models a qualified resource type" do - it "should set its type to the fully qualified resource type" do - Puppet::Resource.new("bar", "/my/file", :namespaces => %w{foo}).type.should == "Foo::Bar" - end - - it "should be able to find the resource type" do - Puppet::Resource.new("bar", "/my/file", :namespaces => %w{foo}).resource_type.should equal(@type) - end - end - end - - describe "that does not exist" do - it "should set its resource type to the capitalized resource type name" do - Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" - end - end - end - - describe "when modeling a node" do - # Life's easier with nodes, because they can't be qualified. - it "should set its type to 'Node' and its title to the provided title" do - node = Puppet::Resource.new("node", "foo") - node.type.should == "Node" - node.title.should == "foo" - end - end - - describe "when modeling a class" do - it "should set its type to 'Class'" do - Puppet::Resource.new("class", "foo").type.should == "Class" - end - - describe "that exists" do - before do - @type = Puppet::Resource::Type.new(:hostclass, "foo::bar") - Puppet::Node::Environment.new.known_resource_types.add @type - end - - it "should set its title to the capitalized, fully qualified resource type" do - Puppet::Resource.new("class", "foo::bar").title.should == "Foo::Bar" - end - - it "should be able to find the resource type" do - Puppet::Resource.new("class", "foo::bar").resource_type.should equal(@type) - end - - describe "and the resource is unqualified and models a qualified class" do - it "should set its title to the fully qualified resource type" do - Puppet::Resource.new("class", "bar", :namespaces => %w{foo}).title.should == "Foo::Bar" - end - - it "should be able to find the resource type" do - Puppet::Resource.new("class", "bar", :namespaces => %w{foo}).resource_type.should equal(@type) - end - - it "should set its type to 'Class'" do - Puppet::Resource.new("class", "bar", :namespaces => %w{foo}).type.should == "Class" - end - end - end - - describe "that does not exist" do - it "should set its type to 'Class' and its title to the capitalized provided name" do - klass = Puppet::Resource.new("class", "foo::bar") - klass.type.should == "Class" - klass.title.should == "Foo::Bar" - end - end - - describe "and its name is set to the empty string" do - it "should set its title to :main" do - Puppet::Resource.new("class", "").title.should == :main - end - - describe "and a class exists whose name is the empty string" do # this was a bit tough to track down - it "should set its title to :main" do - @type = Puppet::Resource::Type.new(:hostclass, "") - Puppet::Node::Environment.new.known_resource_types.add @type - - Puppet::Resource.new("class", "").title.should == :main - end - end - end - - describe "and its name is set to :main" do - it "should set its title to :main" do - Puppet::Resource.new("class", :main).title.should == :main - end - - describe "and a class exists whose name is the empty string" do # this was a bit tough to track down - it "should set its title to :main" do - @type = Puppet::Resource::Type.new(:hostclass, "") - Puppet::Node::Environment.new.known_resource_types.add @type - - Puppet::Resource.new("class", :main).title.should == :main - end - end - end - end - end - - it "should return nil when looking up resource types that don't exist" do - Puppet::Resource.new("foobar", "bar").resource_type.should be_nil - end - - it "should not fail when an invalid parameter is used and strict mode is disabled" do - type = Puppet::Resource::Type.new(:definition, "foobar") - Puppet::Node::Environment.new.known_resource_types.add type - resource = Puppet::Resource.new("foobar", "/my/file") - resource[:yay] = true - end - - it "should be considered equivalent to another resource if their type and title match and no parameters are set" do - Puppet::Resource.new("file", "/f").should == Puppet::Resource.new("file", "/f") - end - - it "should be considered equivalent to another resource if their type, title, and parameters are equal" do - Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}).should == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) - end - - it "should not be considered equivalent to another resource if their type and title match but parameters are different" do - Puppet::Resource.new("file", "/f", :parameters => {:fee => "baz"}).should_not == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) - end - - it "should not be considered equivalent to a non-resource" do - Puppet::Resource.new("file", "/f").should_not == "foo" - end - - it "should not be considered equivalent to another resource if their types do not match" do - Puppet::Resource.new("file", "/f").should_not == Puppet::Resource.new("exec", "/f") - end - - it "should not be considered equivalent to another resource if their titles do not match" do - Puppet::Resource.new("file", "/foo").should_not == Puppet::Resource.new("file", "/f") - end - - describe "when referring to a resource with name canonicalization" do - it "should canonicalize its own name" do - res = Puppet::Resource.new("file", "/path/") - res.uniqueness_key.should == ["/path"] - res.ref.should == "File[/path/]" - end - end - - describe "when running in strict mode" do - it "should be strict" do - Puppet::Resource.new("file", "/path", :strict => true).should be_strict - end - - it "should fail if invalid parameters are used" do - lambda { Puppet::Resource.new("file", "/path", :strict => true, :parameters => {:nosuchparam => "bar"}) }.should raise_error - end - - it "should fail if the resource type cannot be resolved" do - lambda { Puppet::Resource.new("nosuchtype", "/path", :strict => true) }.should raise_error - end - end - - describe "when managing parameters" do - before do - @resource = Puppet::Resource.new("file", "/my/file") - end - - it "should correctly detect when provided parameters are not valid for builtin types" do - Puppet::Resource.new("file", "/my/file").should_not be_valid_parameter("foobar") - end - - it "should correctly detect when provided parameters are valid for builtin types" do - Puppet::Resource.new("file", "/my/file").should be_valid_parameter("mode") - end - - it "should correctly detect when provided parameters are not valid for defined resource types" do - type = Puppet::Resource::Type.new(:definition, "foobar") - Puppet::Node::Environment.new.known_resource_types.add type - Puppet::Resource.new("foobar", "/my/file").should_not be_valid_parameter("myparam") - end - - it "should correctly detect when provided parameters are valid for defined resource types" do - type = Puppet::Resource::Type.new(:definition, "foobar", :arguments => {"myparam" => nil}) - Puppet::Node::Environment.new.known_resource_types.add type - Puppet::Resource.new("foobar", "/my/file").should be_valid_parameter("myparam") - end - - it "should allow setting and retrieving of parameters" do - @resource[:foo] = "bar" - @resource[:foo].should == "bar" - end - - it "should allow setting of parameters at initialization" do - Puppet::Resource.new("file", "/my/file", :parameters => {:foo => "bar"})[:foo].should == "bar" - end - - it "should canonicalize retrieved parameter names to treat symbols and strings equivalently" do - @resource[:foo] = "bar" - @resource["foo"].should == "bar" - end - - it "should canonicalize set parameter names to treat symbols and strings equivalently" do - @resource["foo"] = "bar" - @resource[:foo].should == "bar" - end - - it "should set the namevar when asked to set the name" do - resource = Puppet::Resource.new("user", "bob") - Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] - resource[:name] = "bob" - resource[:myvar].should == "bob" - end - - it "should return the namevar when asked to return the name" do - resource = Puppet::Resource.new("user", "bob") - Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] - resource[:myvar] = "test" - resource[:name].should == "test" - end - - it "should be able to set the name for non-builtin types" do - resource = Puppet::Resource.new(:foo, "bar") - resource[:name] = "eh" - lambda { resource[:name] = "eh" }.should_not raise_error - end - - it "should be able to return the name for non-builtin types" do - resource = Puppet::Resource.new(:foo, "bar") - resource[:name] = "eh" - resource[:name].should == "eh" - end - - it "should be able to iterate over parameters" do - @resource[:foo] = "bar" - @resource[:fee] = "bare" - params = {} - @resource.each do |key, value| - params[key] = value - end - params.should == {:foo => "bar", :fee => "bare"} - end - - it "should include Enumerable" do - @resource.class.ancestors.should be_include(Enumerable) - end - - it "should have a method for testing whether a parameter is included" do - @resource[:foo] = "bar" - @resource.should be_has_key(:foo) - @resource.should_not be_has_key(:eh) - end - - it "should have a method for providing the list of parameters" do - @resource[:foo] = "bar" - @resource[:bar] = "foo" - keys = @resource.keys - keys.should be_include(:foo) - keys.should be_include(:bar) - end - - it "should have a method for providing the number of parameters" do - @resource[:foo] = "bar" - @resource.length.should == 1 - end - - it "should have a method for deleting parameters" do - @resource[:foo] = "bar" - @resource.delete(:foo) - @resource[:foo].should be_nil - end - - it "should have a method for testing whether the parameter list is empty" do - @resource.should be_empty - @resource[:foo] = "bar" - @resource.should_not be_empty - end - - it "should be able to produce a hash of all existing parameters" do - @resource[:foo] = "bar" - @resource[:fee] = "yay" - - hash = @resource.to_hash - hash[:foo].should == "bar" - hash[:fee].should == "yay" - end - - it "should not provide direct access to the internal parameters hash when producing a hash" do - hash = @resource.to_hash - hash[:foo] = "bar" - @resource[:foo].should be_nil - end - - it "should use the title as the namevar to the hash if no namevar is present" do - resource = Puppet::Resource.new("user", "bob") - Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] - resource.to_hash[:myvar].should == "bob" - end - - it "should set :name to the title if :name is not present for non-builtin types" do - krt = Puppet::Resource::TypeCollection.new("myenv") - krt.add Puppet::Resource::Type.new(:definition, :foo) - resource = Puppet::Resource.new :foo, "bar" - resource.stubs(:known_resource_types).returns krt - resource.to_hash[:name].should == "bar" - end - end - - describe "when serializing" do - before do - @resource = Puppet::Resource.new("file", "/my/file") - @resource["one"] = "test" - @resource["two"] = "other" - end - - it "should be able to be dumped to yaml" do - proc { YAML.dump(@resource) }.should_not raise_error - end - - it "should produce an equivalent yaml object" do - text = YAML.dump(@resource) - - newresource = YAML.load(text) - newresource.title.should == @resource.title - newresource.type.should == @resource.type - %w{one two}.each do |param| - newresource[param].should == @resource[param] - end - end - end - - describe "when converting to a RAL resource" do - it "should use the resource type's :new method to create the resource if the resource is of a builtin type" do - resource = Puppet::Resource.new("file", @basepath+"/my/file") - result = resource.to_ral - result.should be_instance_of(Puppet::Type.type(:file)) - result[:path].should == @basepath+"/my/file" - end - - it "should convert to a component instance if the resource type is not of a builtin type" do - resource = Puppet::Resource.new("foobar", "somename") - result = resource.to_ral - - result.should be_instance_of(Puppet::Type.type(:component)) - result.title.should == "Foobar[somename]" - end - end - - it "should be able to convert itself to Puppet code" do - Puppet::Resource.new("one::two", "/my/file").should respond_to(:to_manifest) - end - - describe "when converting to puppet code" do - before do - @resource = Puppet::Resource.new("one::two", "/my/file", :parameters => {:noop => true, :foo => %w{one two}}) - end - - it "should print the type and title" do - @resource.to_manifest.should be_include("one::two { '/my/file':\n") - end - - it "should print each parameter, with the value single-quoted" do - @resource.to_manifest.should be_include(" noop => 'true'") - end - - it "should print array values appropriately" do - @resource.to_manifest.should be_include(" foo => ['one','two']") - end - end - - it "should be able to convert itself to a TransObject instance" do - Puppet::Resource.new("one::two", "/my/file").should respond_to(:to_trans) - end - - describe "when converting to a TransObject" do - describe "and the resource is not an instance of a builtin type" do - before do - @resource = Puppet::Resource.new("foo", "bar") - end - - it "should return a simple TransBucket if it is not an instance of a builtin type" do - bucket = @resource.to_trans - bucket.should be_instance_of(Puppet::TransBucket) - bucket.type.should == @resource.type - bucket.name.should == @resource.title - end - - it "should return a simple TransBucket if it is a stage" do - @resource = Puppet::Resource.new("stage", "bar") - bucket = @resource.to_trans - bucket.should be_instance_of(Puppet::TransBucket) - bucket.type.should == @resource.type - bucket.name.should == @resource.title - end - - it "should copy over the resource's file" do - @resource.file = "/foo/bar" - @resource.to_trans.file.should == "/foo/bar" - end - - it "should copy over the resource's line" do - @resource.line = 50 - @resource.to_trans.line.should == 50 - end - end - - describe "and the resource is an instance of a builtin type" do - before do - @resource = Puppet::Resource.new("file", "bar") - end - - it "should return a TransObject if it is an instance of a builtin resource type" do - trans = @resource.to_trans - trans.should be_instance_of(Puppet::TransObject) - trans.type.should == "file" - trans.name.should == @resource.title - end - - it "should copy over the resource's file" do - @resource.file = "/foo/bar" - @resource.to_trans.file.should == "/foo/bar" - end - - it "should copy over the resource's line" do - @resource.line = 50 - @resource.to_trans.line.should == 50 - end - - # Only TransObjects support tags, annoyingly - it "should copy over the resource's tags" do - @resource.tag "foo" - @resource.to_trans.tags.should == @resource.tags - end - - it "should copy the resource's parameters into the transobject and convert the parameter name to a string" do - @resource[:foo] = "bar" - @resource.to_trans["foo"].should == "bar" - end - - it "should be able to copy arrays of values" do - @resource[:foo] = %w{yay fee} - @resource.to_trans["foo"].should == %w{yay fee} - end - - it "should reduce single-value arrays to just a value" do - @resource[:foo] = %w{yay} - @resource.to_trans["foo"].should == "yay" - end - - it "should convert resource references into the backward-compatible form" do - @resource[:foo] = Puppet::Resource.new(:file, "/f") - @resource.to_trans["foo"].should == %w{File /f} - end - - it "should convert resource references into the backward-compatible form even when within arrays" do - @resource[:foo] = ["a", Puppet::Resource.new(:file, "/f")] - @resource.to_trans["foo"].should == ["a", %w{File /f}] - end - end - end - - describe "when converting to pson" do - confine "Missing 'pson' library" => Puppet.features.pson? - - def pson_output_should - @resource.class.expects(:pson_create).with { |hash| yield hash } - end - - it "should include the pson util module" do - Puppet::Resource.singleton_class.ancestors.should be_include(Puppet::Util::Pson) - end - - # LAK:NOTE For all of these tests, we convert back to the resource so we can - # trap the actual data structure then. - - it "should set its type to the provided type" do - Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).type.should == "File" - end - - it "should set its title to the provided title" do - Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).title.should == "/foo" - end - - it "should include all tags from the resource" do - resource = Puppet::Resource.new("File", "/foo") - resource.tag("yay") - - Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).tags.should == resource.tags - end - - it "should include the file if one is set" do - resource = Puppet::Resource.new("File", "/foo") - resource.file = "/my/file" - - Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).file.should == "/my/file" - end - - it "should include the line if one is set" do - resource = Puppet::Resource.new("File", "/foo") - resource.line = 50 - - Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).line.should == 50 - end - - it "should include the 'exported' value if one is set" do - resource = Puppet::Resource.new("File", "/foo") - resource.exported = true - - Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported.should be_true - end - - it "should set 'exported' to false if no value is set" do - resource = Puppet::Resource.new("File", "/foo") - - Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported.should be_false - end - - it "should set all of its parameters as the 'parameters' entry" do - resource = Puppet::Resource.new("File", "/foo") - resource[:foo] = %w{bar eh} - resource[:fee] = %w{baz} - - result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) - result["foo"].should == %w{bar eh} - result["fee"].should == %w{baz} - end - - it "should serialize relationships as reference strings" do - resource = Puppet::Resource.new("File", "/foo") - resource[:requires] = Puppet::Resource.new("File", "/bar") - result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) - result[:requires].should == "File[/bar]" - end - end - - describe "when converting from pson" do - confine "Missing 'pson' library" => Puppet.features.pson? - - def pson_result_should - Puppet::Resource.expects(:new).with { |hash| yield hash } - end - - before do - @data = { - 'type' => "file", - 'title' => @basepath+"/yay", - } - end - - it "should set its type to the provided type" do - Puppet::Resource.from_pson(@data).type.should == "File" - end - - it "should set its title to the provided title" do - Puppet::Resource.from_pson(@data).title.should == @basepath+"/yay" - end - - it "should tag the resource with any provided tags" do - @data['tags'] = %w{foo bar} - resource = Puppet::Resource.from_pson(@data) - resource.tags.should be_include("foo") - resource.tags.should be_include("bar") - end - - it "should set its file to the provided file" do - @data['file'] = "/foo/bar" - Puppet::Resource.from_pson(@data).file.should == "/foo/bar" - end - - it "should set its line to the provided line" do - @data['line'] = 50 - Puppet::Resource.from_pson(@data).line.should == 50 - end - - it "should 'exported' to true if set in the pson data" do - @data['exported'] = true - Puppet::Resource.from_pson(@data).exported.should be_true - end - - it "should 'exported' to false if not set in the pson data" do - Puppet::Resource.from_pson(@data).exported.should be_false - end - - it "should fail if no title is provided" do - @data.delete('title') - lambda { Puppet::Resource.from_pson(@data) }.should raise_error(ArgumentError) - end - - it "should fail if no type is provided" do - @data.delete('type') - lambda { Puppet::Resource.from_pson(@data) }.should raise_error(ArgumentError) - end - - it "should set each of the provided parameters" do - @data['parameters'] = {'foo' => %w{one two}, 'fee' => %w{three four}} - resource = Puppet::Resource.from_pson(@data) - resource['foo'].should == %w{one two} - resource['fee'].should == %w{three four} - end - - it "should convert single-value array parameters to normal values" do - @data['parameters'] = {'foo' => %w{one}} - resource = Puppet::Resource.from_pson(@data) - resource['foo'].should == %w{one} - end - end - - describe "it should implement to_resource" do - resource = Puppet::Resource.new("file", "/my/file") - resource.to_resource.should == resource - end - - describe "because it is an indirector model" do - it "should include Puppet::Indirector" do - Puppet::Resource.should be_is_a(Puppet::Indirector) - end - - it "should have a default terminus" do - Puppet::Resource.indirection.terminus_class.should == :ral - end - - it "should have a name" do - Puppet::Resource.new("file", "/my/file").name.should == "File//my/file" - end - end - - describe "when resolving resources with a catalog" do - it "should resolve all resources using the catalog" do - catalog = mock 'catalog' - resource = Puppet::Resource.new("foo::bar", "yay") - resource.catalog = catalog - - catalog.expects(:resource).with("Foo::Bar[yay]").returns(:myresource) - - resource.resolve.should == :myresource - end - end - - describe "when generating the uniqueness key" do - it "should include all of the key_attributes in alphabetical order by attribute name" do - Puppet::Type.type(:file).stubs(:key_attributes).returns [:myvar, :owner, :path] - Puppet::Type.type(:file).stubs(:title_patterns).returns( - [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ] - ) - res = Puppet::Resource.new("file", "/my/file", :parameters => {:owner => 'root', :content => 'hello'}) - res.uniqueness_key.should == [ nil, 'root', '/my/file'] - end - end -end diff --git a/spec/unit/run_spec.rb b/spec/unit/run_spec.rb new file mode 100755 index 000000000..4c5f6b1af --- /dev/null +++ b/spec/unit/run_spec.rb @@ -0,0 +1,134 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' +require 'puppet/agent' +require 'puppet/run' + +describe Puppet::Run do + before do + @runner = Puppet::Run.new + end + + it "should indirect :run" do + Puppet::Run.indirection.name.should == :run + end + + it "should use a configurer agent as its agent" do + agent = mock 'agent' + Puppet::Agent.expects(:new).with(Puppet::Configurer).returns agent + + @runner.agent.should equal(agent) + end + + it "should accept options at initialization" do + lambda { Puppet::Run.new :background => true }.should_not raise_error + end + + it "should default to running in the foreground" do + Puppet::Run.new.should_not be_background + end + + it "should default to its options being an empty hash" do + Puppet::Run.new.options.should == {} + end + + it "should accept :tags for the agent" do + Puppet::Run.new(:tags => "foo").options[:tags].should == "foo" + end + + it "should accept :ignoreschedules for the agent" do + Puppet::Run.new(:ignoreschedules => true).options[:ignoreschedules].should be_true + end + + it "should accept an option to configure it to run in the background" do + Puppet::Run.new(:background => true).should be_background + end + + it "should retain the background option" do + Puppet::Run.new(:background => true).options[:background].should be_nil + end + + it "should not accept arbitrary options" do + lambda { Puppet::Run.new(:foo => true) }.should raise_error(ArgumentError) + end + + describe "when asked to run" do + before do + @agent = stub 'agent', :run => nil, :running? => false + @runner.stubs(:agent).returns @agent + end + + it "should run its agent" do + agent = stub 'agent2', :running? => false + @runner.stubs(:agent).returns agent + + agent.expects(:run) + + @runner.run + end + + it "should pass any of its options on to the agent" do + @runner.stubs(:options).returns(:foo => :bar) + @agent.expects(:run).with(:foo => :bar) + + @runner.run + end + + it "should log its run using the provided options" do + @runner.expects(:log_run) + + @runner.run + end + + it "should set its status to 'already_running' if the agent is already running" do + @agent.expects(:running?).returns true + + @runner.run + + @runner.status.should == "running" + end + + it "should set its status to 'success' if the agent is run" do + @agent.expects(:running?).returns false + + @runner.run + + @runner.status.should == "success" + end + + it "should run the agent in a thread if asked to run it in the background" do + Thread.expects(:new) + + @runner.expects(:background?).returns true + + @agent.expects(:run).never # because our thread didn't yield + + @runner.run + end + + it "should run the agent directly if asked to run it in the foreground" do + Thread.expects(:new).never + + @runner.expects(:background?).returns false + @agent.expects(:run) + + @runner.run + end + end + + describe ".from_pson" do + it "should accept a hash of options, and pass them with symbolified keys to new" do + options = { + "tags" => "whatever", + "background" => true, + } + + Puppet::Run.expects(:new).with({ + :tags => "whatever", + :background => true, + }) + + Puppet::Run.from_pson(options) + end + end +end diff --git a/spec/unit/run_spec_spec.rb b/spec/unit/run_spec_spec.rb deleted file mode 100755 index 4c5f6b1af..000000000 --- a/spec/unit/run_spec_spec.rb +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' -require 'puppet/agent' -require 'puppet/run' - -describe Puppet::Run do - before do - @runner = Puppet::Run.new - end - - it "should indirect :run" do - Puppet::Run.indirection.name.should == :run - end - - it "should use a configurer agent as its agent" do - agent = mock 'agent' - Puppet::Agent.expects(:new).with(Puppet::Configurer).returns agent - - @runner.agent.should equal(agent) - end - - it "should accept options at initialization" do - lambda { Puppet::Run.new :background => true }.should_not raise_error - end - - it "should default to running in the foreground" do - Puppet::Run.new.should_not be_background - end - - it "should default to its options being an empty hash" do - Puppet::Run.new.options.should == {} - end - - it "should accept :tags for the agent" do - Puppet::Run.new(:tags => "foo").options[:tags].should == "foo" - end - - it "should accept :ignoreschedules for the agent" do - Puppet::Run.new(:ignoreschedules => true).options[:ignoreschedules].should be_true - end - - it "should accept an option to configure it to run in the background" do - Puppet::Run.new(:background => true).should be_background - end - - it "should retain the background option" do - Puppet::Run.new(:background => true).options[:background].should be_nil - end - - it "should not accept arbitrary options" do - lambda { Puppet::Run.new(:foo => true) }.should raise_error(ArgumentError) - end - - describe "when asked to run" do - before do - @agent = stub 'agent', :run => nil, :running? => false - @runner.stubs(:agent).returns @agent - end - - it "should run its agent" do - agent = stub 'agent2', :running? => false - @runner.stubs(:agent).returns agent - - agent.expects(:run) - - @runner.run - end - - it "should pass any of its options on to the agent" do - @runner.stubs(:options).returns(:foo => :bar) - @agent.expects(:run).with(:foo => :bar) - - @runner.run - end - - it "should log its run using the provided options" do - @runner.expects(:log_run) - - @runner.run - end - - it "should set its status to 'already_running' if the agent is already running" do - @agent.expects(:running?).returns true - - @runner.run - - @runner.status.should == "running" - end - - it "should set its status to 'success' if the agent is run" do - @agent.expects(:running?).returns false - - @runner.run - - @runner.status.should == "success" - end - - it "should run the agent in a thread if asked to run it in the background" do - Thread.expects(:new) - - @runner.expects(:background?).returns true - - @agent.expects(:run).never # because our thread didn't yield - - @runner.run - end - - it "should run the agent directly if asked to run it in the foreground" do - Thread.expects(:new).never - - @runner.expects(:background?).returns false - @agent.expects(:run) - - @runner.run - end - end - - describe ".from_pson" do - it "should accept a hash of options, and pass them with symbolified keys to new" do - options = { - "tags" => "whatever", - "background" => true, - } - - Puppet::Run.expects(:new).with({ - :tags => "whatever", - :background => true, - }) - - Puppet::Run.from_pson(options) - end - end -end diff --git a/spec/unit/simple_graph_spec.rb b/spec/unit/simple_graph_spec.rb new file mode 100755 index 000000000..234a2fc1f --- /dev/null +++ b/spec/unit/simple_graph_spec.rb @@ -0,0 +1,544 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-11-1. +# Copyright (c) 2006. All rights reserved. + +require File.dirname(__FILE__) + '/../spec_helper' +require 'puppet/simple_graph' + +describe Puppet::SimpleGraph do + it "should return the number of its vertices as its length" do + @graph = Puppet::SimpleGraph.new + @graph.add_vertex("one") + @graph.add_vertex("two") + @graph.size.should == 2 + end + + it "should consider itself a directed graph" do + Puppet::SimpleGraph.new.directed?.should be_true + end + + it "should provide a method for reversing the graph" do + @graph = Puppet::SimpleGraph.new + @graph.add_edge(:one, :two) + @graph.reversal.edge?(:two, :one).should be_true + end + + it "should be able to produce a dot graph" do + @graph = Puppet::SimpleGraph.new + @graph.add_edge(:one, :two) + + proc { @graph.to_dot_graph }.should_not raise_error + end + + it "should always put its edges first when printing yaml" do + @graph = Puppet::SimpleGraph.new + @graph.add_edge(:one, :two) + @graph.to_yaml_properties[0].should == "@edges" + end + + describe "when managing vertices" do + before do + @graph = Puppet::SimpleGraph.new + end + + it "should provide a method to add a vertex" do + @graph.add_vertex(:test) + @graph.vertex?(:test).should be_true + end + + it "should reset its reversed graph when vertices are added" do + rev = @graph.reversal + @graph.add_vertex(:test) + @graph.reversal.should_not equal(rev) + end + + it "should ignore already-present vertices when asked to add a vertex" do + @graph.add_vertex(:test) + proc { @graph.add_vertex(:test) }.should_not raise_error + end + + it "should return true when asked if a vertex is present" do + @graph.add_vertex(:test) + @graph.vertex?(:test).should be_true + end + + it "should return false when asked if a non-vertex is present" do + @graph.vertex?(:test).should be_false + end + + it "should return all set vertices when asked" do + @graph.add_vertex(:one) + @graph.add_vertex(:two) + @graph.vertices.length.should == 2 + @graph.vertices.should include(:one) + @graph.vertices.should include(:two) + end + + it "should remove a given vertex when asked" do + @graph.add_vertex(:one) + @graph.remove_vertex!(:one) + @graph.vertex?(:one).should be_false + end + + it "should do nothing when a non-vertex is asked to be removed" do + proc { @graph.remove_vertex!(:one) }.should_not raise_error + end + end + + describe "when managing edges" do + before do + @graph = Puppet::SimpleGraph.new + end + + it "should provide a method to test whether a given vertex pair is an edge" do + @graph.should respond_to(:edge?) + end + + it "should reset its reversed graph when edges are added" do + rev = @graph.reversal + @graph.add_edge(:one, :two) + @graph.reversal.should_not equal(rev) + end + + it "should provide a method to add an edge as an instance of the edge class" do + edge = Puppet::Relationship.new(:one, :two) + @graph.add_edge(edge) + @graph.edge?(:one, :two).should be_true + end + + it "should provide a method to add an edge by specifying the two vertices" do + @graph.add_edge(:one, :two) + @graph.edge?(:one, :two).should be_true + end + + it "should provide a method to add an edge by specifying the two vertices and a label" do + @graph.add_edge(:one, :two, :callback => :awesome) + @graph.edge?(:one, :two).should be_true + end + + it "should provide a method for retrieving an edge label" do + edge = Puppet::Relationship.new(:one, :two, :callback => :awesome) + @graph.add_edge(edge) + @graph.edge_label(:one, :two).should == {:callback => :awesome} + end + + it "should provide a method for retrieving an edge" do + edge = Puppet::Relationship.new(:one, :two) + @graph.add_edge(edge) + @graph.edge(:one, :two).should equal(edge) + end + + it "should add the edge source as a vertex if it is not already" do + edge = Puppet::Relationship.new(:one, :two) + @graph.add_edge(edge) + @graph.vertex?(:one).should be_true + end + + it "should add the edge target as a vertex if it is not already" do + edge = Puppet::Relationship.new(:one, :two) + @graph.add_edge(edge) + @graph.vertex?(:two).should be_true + end + + it "should return all edges as edge instances when asked" do + one = Puppet::Relationship.new(:one, :two) + two = Puppet::Relationship.new(:two, :three) + @graph.add_edge(one) + @graph.add_edge(two) + edges = @graph.edges + edges.should be_instance_of(Array) + edges.length.should == 2 + edges.should include(one) + edges.should include(two) + end + + it "should remove an edge when asked" do + edge = Puppet::Relationship.new(:one, :two) + @graph.add_edge(edge) + @graph.remove_edge!(edge) + @graph.edge?(edge.source, edge.target).should be_false + end + + it "should remove all related edges when a vertex is removed" do + one = Puppet::Relationship.new(:one, :two) + two = Puppet::Relationship.new(:two, :three) + @graph.add_edge(one) + @graph.add_edge(two) + @graph.remove_vertex!(:two) + @graph.edge?(:one, :two).should be_false + @graph.edge?(:two, :three).should be_false + @graph.edges.length.should == 0 + end + end + + describe "when finding adjacent vertices" do + before do + @graph = Puppet::SimpleGraph.new + @one_two = Puppet::Relationship.new(:one, :two) + @two_three = Puppet::Relationship.new(:two, :three) + @one_three = Puppet::Relationship.new(:one, :three) + @graph.add_edge(@one_two) + @graph.add_edge(@one_three) + @graph.add_edge(@two_three) + end + + it "should return adjacent vertices" do + adj = @graph.adjacent(:one) + adj.should be_include(:three) + adj.should be_include(:two) + end + + it "should default to finding :out vertices" do + @graph.adjacent(:two).should == [:three] + end + + it "should support selecting :in vertices" do + @graph.adjacent(:two, :direction => :in).should == [:one] + end + + it "should default to returning the matching vertices as an array of vertices" do + @graph.adjacent(:two).should == [:three] + end + + it "should support returning an array of matching edges" do + @graph.adjacent(:two, :type => :edges).should == [@two_three] + end + + # Bug #2111 + it "should not consider a vertex adjacent just because it was asked about previously" do + @graph = Puppet::SimpleGraph.new + @graph.add_vertex("a") + @graph.add_vertex("b") + @graph.edge?("a", "b") + @graph.adjacent("a").should == [] + end + end + + describe "when clearing" do + before do + @graph = Puppet::SimpleGraph.new + one = Puppet::Relationship.new(:one, :two) + two = Puppet::Relationship.new(:two, :three) + @graph.add_edge(one) + @graph.add_edge(two) + + @graph.clear + end + + it "should remove all vertices" do + @graph.vertices.should be_empty + end + + it "should remove all edges" do + @graph.edges.should be_empty + end + end + + describe "when reversing graphs" do + before do + @graph = Puppet::SimpleGraph.new + end + + it "should provide a method for reversing the graph" do + @graph.add_edge(:one, :two) + @graph.reversal.edge?(:two, :one).should be_true + end + + it "should add all vertices to the reversed graph" do + @graph.add_edge(:one, :two) + @graph.vertex?(:one).should be_true + @graph.vertex?(:two).should be_true + end + + it "should retain labels on edges" do + @graph.add_edge(:one, :two, :callback => :awesome) + edge = @graph.reversal.edge(:two, :one) + edge.label.should == {:callback => :awesome} + end + end + + describe "when sorting the graph" do + before do + @graph = Puppet::SimpleGraph.new + end + + def add_edges(hash) + hash.each do |a,b| + @graph.add_edge(a, b) + end + end + + it "should sort the graph topologically" do + add_edges :a => :b, :b => :c + @graph.topsort.should == [:a, :b, :c] + end + + it "should fail on two-vertex loops" do + add_edges :a => :b, :b => :a + proc { @graph.topsort }.should raise_error(Puppet::Error) + end + + it "should fail on multi-vertex loops" do + add_edges :a => :b, :b => :c, :c => :a + proc { @graph.topsort }.should raise_error(Puppet::Error) + end + + it "should fail when a larger tree contains a small cycle" do + add_edges :a => :b, :b => :a, :c => :a, :d => :c + proc { @graph.topsort }.should raise_error(Puppet::Error) + end + + it "should succeed on trees with no cycles" do + add_edges :a => :b, :b => :e, :c => :a, :d => :c + proc { @graph.topsort }.should_not raise_error + end + + # Our graph's add_edge method is smart enough not to add + # duplicate edges, so we use the objects, which it doesn't + # check. + it "should be able to sort graphs with duplicate edges" do + one = Puppet::Relationship.new(:a, :b) + @graph.add_edge(one) + two = Puppet::Relationship.new(:a, :b) + @graph.add_edge(two) + proc { @graph.topsort }.should_not raise_error + end + end + + describe "when writing dot files" do + before do + @graph = Puppet::SimpleGraph.new + @name = :test + @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") + end + + it "should only write when graphing is enabled" do + File.expects(:open).with(@file).never + Puppet[:graph] = false + @graph.write_graph(@name) + end + + it "should write a dot file based on the passed name" do + File.expects(:open).with(@file, "w").yields(stub("file", :puts => nil)) + @graph.expects(:to_dot).with("name" => @name.to_s.capitalize) + Puppet[:graph] = true + @graph.write_graph(@name) + end + + after do + Puppet.settings.clear + end + end + + describe Puppet::SimpleGraph do + before do + @graph = Puppet::SimpleGraph.new + end + + it "should correctly clear vertices and edges when asked" do + @graph.add_edge("a", "b") + @graph.add_vertex "c" + @graph.clear + @graph.vertices.should be_empty + @graph.edges.should be_empty + end + end + + describe "when matching edges" do + before do + @graph = Puppet::SimpleGraph.new + @event = Puppet::Transaction::Event.new(:name => :yay, :resource => "a") + @none = Puppet::Transaction::Event.new(:name => :NONE, :resource => "a") + + @edges = {} + @edges["a/b"] = Puppet::Relationship.new("a", "b", {:event => :yay, :callback => :refresh}) + @edges["a/c"] = Puppet::Relationship.new("a", "c", {:event => :yay, :callback => :refresh}) + @graph.add_edge(@edges["a/b"]) + end + + it "should match edges whose source matches the source of the event" do + @graph.matching_edges(@event).should == [@edges["a/b"]] + end + + it "should match always match nothing when the event is :NONE" do + @graph.matching_edges(@none).should be_empty + end + + it "should match multiple edges" do + @graph.add_edge(@edges["a/c"]) + edges = @graph.matching_edges(@event) + edges.should be_include(@edges["a/b"]) + edges.should be_include(@edges["a/c"]) + end + end + + describe "when determining dependencies" do + before do + @graph = Puppet::SimpleGraph.new + + @graph.add_edge("a", "b") + @graph.add_edge("a", "c") + @graph.add_edge("b", "d") + end + + it "should find all dependents when they are on multiple levels" do + @graph.dependents("a").sort.should == %w{b c d}.sort + end + + it "should find single dependents" do + @graph.dependents("b").sort.should == %w{d}.sort + end + + it "should return an empty array when there are no dependents" do + @graph.dependents("c").sort.should == [].sort + end + + it "should find all dependencies when they are on multiple levels" do + @graph.dependencies("d").sort.should == %w{a b} + end + + it "should find single dependencies" do + @graph.dependencies("c").sort.should == %w{a} + end + + it "should return an empty array when there are no dependencies" do + @graph.dependencies("a").sort.should == [] + end + end + + require 'puppet/util/graph' + + class Container + include Puppet::Util::Graph + include Enumerable + attr_accessor :name + def each + @children.each do |c| yield c end + end + + def initialize(name, ary) + @name = name + @children = ary + end + + def push(*ary) + ary.each { |c| @children.push(c)} + end + + def to_s + @name + end + end + + describe "when splicing the graph" do + def container_graph + @one = Container.new("one", %w{a b}) + @two = Container.new("two", ["c", "d"]) + @three = Container.new("three", ["i", "j"]) + @middle = Container.new("middle", ["e", "f", @two]) + @top = Container.new("top", ["g", "h", @middle, @one, @three]) + @empty = Container.new("empty", []) + + @stage = Puppet::Type.type(:stage).new(:name => "foo") + + @contgraph = @top.to_graph + + # We have to add the container to the main graph, else it won't + # be spliced in the dependency graph. + @contgraph.add_vertex(@empty) + end + + def dependency_graph + @depgraph = Puppet::SimpleGraph.new + @contgraph.vertices.each do |v| + @depgraph.add_vertex(v) + end + + # We have to specify a relationship to our empty container, else it + # never makes it into the dep graph in the first place. + {@one => @two, "f" => "c", "h" => @middle, "c" => @empty}.each do |source, target| + @depgraph.add_edge(source, target, :callback => :refresh) + end + end + + def splice + @depgraph.splice!(@contgraph, Container) + end + + before do + container_graph + dependency_graph + splice + end + + # This is the real heart of splicing -- replacing all containers in + # our relationship and exploding their relationships so that each + # relationship to a container gets copied to all of its children. + it "should remove all Container objects from the dependency graph" do + @depgraph.vertices.find_all { |v| v.is_a?(Container) }.should be_empty + end + + # This is a bit hideous, but required to make stages work with relationships - they're + # the top of the graph. + it "should remove all Stage resources from the dependency graph" do + @depgraph.vertices.find_all { |v| v.is_a?(Puppet::Type.type(:stage)) }.should be_empty + end + + it "should add container relationships to contained objects" do + @contgraph.leaves(@middle).each do |leaf| + @depgraph.should be_edge("h", leaf) + end + end + + it "should explode container-to-container relationships, making edges between all respective contained objects" do + @one.each do |oobj| + @two.each do |tobj| + @depgraph.should be_edge(oobj, tobj) + end + end + end + + it "should no longer contain anything but the non-container objects" do + @depgraph.vertices.find_all { |v| ! v.is_a?(String) }.should be_empty + end + + it "should copy labels" do + @depgraph.edges.each do |edge| + edge.label.should == {:callback => :refresh} + end + end + + it "should not add labels to edges that have none" do + @depgraph.add_edge(@two, @three) + splice + @depgraph.edge_label("c", "i").should == {} + end + + it "should copy labels over edges that have none" do + @depgraph.add_edge("c", @three, {:callback => :refresh}) + splice + # And make sure the label got copied. + @depgraph.edge_label("c", "i").should == {:callback => :refresh} + end + + it "should not replace a label with a nil label" do + # Lastly, add some new label-less edges and make sure the label stays. + @depgraph.add_edge(@middle, @three) + @depgraph.add_edge("c", @three, {:callback => :refresh}) + splice + @depgraph.edge_label("c", "i").should == {:callback => :refresh} + end + + it "should copy labels to all created edges" do + @depgraph.add_edge(@middle, @three) + @depgraph.add_edge("c", @three, {:callback => :refresh}) + splice + @three.each do |child| + edge = Puppet::Relationship.new("c", child) + @depgraph.should be_edge(edge.source, edge.target) + @depgraph.edge_label(edge.source, edge.target).should == {:callback => :refresh} + end + end + end +end diff --git a/spec/unit/simple_graph_spec_spec.rb b/spec/unit/simple_graph_spec_spec.rb deleted file mode 100755 index 234a2fc1f..000000000 --- a/spec/unit/simple_graph_spec_spec.rb +++ /dev/null @@ -1,544 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-11-1. -# Copyright (c) 2006. All rights reserved. - -require File.dirname(__FILE__) + '/../spec_helper' -require 'puppet/simple_graph' - -describe Puppet::SimpleGraph do - it "should return the number of its vertices as its length" do - @graph = Puppet::SimpleGraph.new - @graph.add_vertex("one") - @graph.add_vertex("two") - @graph.size.should == 2 - end - - it "should consider itself a directed graph" do - Puppet::SimpleGraph.new.directed?.should be_true - end - - it "should provide a method for reversing the graph" do - @graph = Puppet::SimpleGraph.new - @graph.add_edge(:one, :two) - @graph.reversal.edge?(:two, :one).should be_true - end - - it "should be able to produce a dot graph" do - @graph = Puppet::SimpleGraph.new - @graph.add_edge(:one, :two) - - proc { @graph.to_dot_graph }.should_not raise_error - end - - it "should always put its edges first when printing yaml" do - @graph = Puppet::SimpleGraph.new - @graph.add_edge(:one, :two) - @graph.to_yaml_properties[0].should == "@edges" - end - - describe "when managing vertices" do - before do - @graph = Puppet::SimpleGraph.new - end - - it "should provide a method to add a vertex" do - @graph.add_vertex(:test) - @graph.vertex?(:test).should be_true - end - - it "should reset its reversed graph when vertices are added" do - rev = @graph.reversal - @graph.add_vertex(:test) - @graph.reversal.should_not equal(rev) - end - - it "should ignore already-present vertices when asked to add a vertex" do - @graph.add_vertex(:test) - proc { @graph.add_vertex(:test) }.should_not raise_error - end - - it "should return true when asked if a vertex is present" do - @graph.add_vertex(:test) - @graph.vertex?(:test).should be_true - end - - it "should return false when asked if a non-vertex is present" do - @graph.vertex?(:test).should be_false - end - - it "should return all set vertices when asked" do - @graph.add_vertex(:one) - @graph.add_vertex(:two) - @graph.vertices.length.should == 2 - @graph.vertices.should include(:one) - @graph.vertices.should include(:two) - end - - it "should remove a given vertex when asked" do - @graph.add_vertex(:one) - @graph.remove_vertex!(:one) - @graph.vertex?(:one).should be_false - end - - it "should do nothing when a non-vertex is asked to be removed" do - proc { @graph.remove_vertex!(:one) }.should_not raise_error - end - end - - describe "when managing edges" do - before do - @graph = Puppet::SimpleGraph.new - end - - it "should provide a method to test whether a given vertex pair is an edge" do - @graph.should respond_to(:edge?) - end - - it "should reset its reversed graph when edges are added" do - rev = @graph.reversal - @graph.add_edge(:one, :two) - @graph.reversal.should_not equal(rev) - end - - it "should provide a method to add an edge as an instance of the edge class" do - edge = Puppet::Relationship.new(:one, :two) - @graph.add_edge(edge) - @graph.edge?(:one, :two).should be_true - end - - it "should provide a method to add an edge by specifying the two vertices" do - @graph.add_edge(:one, :two) - @graph.edge?(:one, :two).should be_true - end - - it "should provide a method to add an edge by specifying the two vertices and a label" do - @graph.add_edge(:one, :two, :callback => :awesome) - @graph.edge?(:one, :two).should be_true - end - - it "should provide a method for retrieving an edge label" do - edge = Puppet::Relationship.new(:one, :two, :callback => :awesome) - @graph.add_edge(edge) - @graph.edge_label(:one, :two).should == {:callback => :awesome} - end - - it "should provide a method for retrieving an edge" do - edge = Puppet::Relationship.new(:one, :two) - @graph.add_edge(edge) - @graph.edge(:one, :two).should equal(edge) - end - - it "should add the edge source as a vertex if it is not already" do - edge = Puppet::Relationship.new(:one, :two) - @graph.add_edge(edge) - @graph.vertex?(:one).should be_true - end - - it "should add the edge target as a vertex if it is not already" do - edge = Puppet::Relationship.new(:one, :two) - @graph.add_edge(edge) - @graph.vertex?(:two).should be_true - end - - it "should return all edges as edge instances when asked" do - one = Puppet::Relationship.new(:one, :two) - two = Puppet::Relationship.new(:two, :three) - @graph.add_edge(one) - @graph.add_edge(two) - edges = @graph.edges - edges.should be_instance_of(Array) - edges.length.should == 2 - edges.should include(one) - edges.should include(two) - end - - it "should remove an edge when asked" do - edge = Puppet::Relationship.new(:one, :two) - @graph.add_edge(edge) - @graph.remove_edge!(edge) - @graph.edge?(edge.source, edge.target).should be_false - end - - it "should remove all related edges when a vertex is removed" do - one = Puppet::Relationship.new(:one, :two) - two = Puppet::Relationship.new(:two, :three) - @graph.add_edge(one) - @graph.add_edge(two) - @graph.remove_vertex!(:two) - @graph.edge?(:one, :two).should be_false - @graph.edge?(:two, :three).should be_false - @graph.edges.length.should == 0 - end - end - - describe "when finding adjacent vertices" do - before do - @graph = Puppet::SimpleGraph.new - @one_two = Puppet::Relationship.new(:one, :two) - @two_three = Puppet::Relationship.new(:two, :three) - @one_three = Puppet::Relationship.new(:one, :three) - @graph.add_edge(@one_two) - @graph.add_edge(@one_three) - @graph.add_edge(@two_three) - end - - it "should return adjacent vertices" do - adj = @graph.adjacent(:one) - adj.should be_include(:three) - adj.should be_include(:two) - end - - it "should default to finding :out vertices" do - @graph.adjacent(:two).should == [:three] - end - - it "should support selecting :in vertices" do - @graph.adjacent(:two, :direction => :in).should == [:one] - end - - it "should default to returning the matching vertices as an array of vertices" do - @graph.adjacent(:two).should == [:three] - end - - it "should support returning an array of matching edges" do - @graph.adjacent(:two, :type => :edges).should == [@two_three] - end - - # Bug #2111 - it "should not consider a vertex adjacent just because it was asked about previously" do - @graph = Puppet::SimpleGraph.new - @graph.add_vertex("a") - @graph.add_vertex("b") - @graph.edge?("a", "b") - @graph.adjacent("a").should == [] - end - end - - describe "when clearing" do - before do - @graph = Puppet::SimpleGraph.new - one = Puppet::Relationship.new(:one, :two) - two = Puppet::Relationship.new(:two, :three) - @graph.add_edge(one) - @graph.add_edge(two) - - @graph.clear - end - - it "should remove all vertices" do - @graph.vertices.should be_empty - end - - it "should remove all edges" do - @graph.edges.should be_empty - end - end - - describe "when reversing graphs" do - before do - @graph = Puppet::SimpleGraph.new - end - - it "should provide a method for reversing the graph" do - @graph.add_edge(:one, :two) - @graph.reversal.edge?(:two, :one).should be_true - end - - it "should add all vertices to the reversed graph" do - @graph.add_edge(:one, :two) - @graph.vertex?(:one).should be_true - @graph.vertex?(:two).should be_true - end - - it "should retain labels on edges" do - @graph.add_edge(:one, :two, :callback => :awesome) - edge = @graph.reversal.edge(:two, :one) - edge.label.should == {:callback => :awesome} - end - end - - describe "when sorting the graph" do - before do - @graph = Puppet::SimpleGraph.new - end - - def add_edges(hash) - hash.each do |a,b| - @graph.add_edge(a, b) - end - end - - it "should sort the graph topologically" do - add_edges :a => :b, :b => :c - @graph.topsort.should == [:a, :b, :c] - end - - it "should fail on two-vertex loops" do - add_edges :a => :b, :b => :a - proc { @graph.topsort }.should raise_error(Puppet::Error) - end - - it "should fail on multi-vertex loops" do - add_edges :a => :b, :b => :c, :c => :a - proc { @graph.topsort }.should raise_error(Puppet::Error) - end - - it "should fail when a larger tree contains a small cycle" do - add_edges :a => :b, :b => :a, :c => :a, :d => :c - proc { @graph.topsort }.should raise_error(Puppet::Error) - end - - it "should succeed on trees with no cycles" do - add_edges :a => :b, :b => :e, :c => :a, :d => :c - proc { @graph.topsort }.should_not raise_error - end - - # Our graph's add_edge method is smart enough not to add - # duplicate edges, so we use the objects, which it doesn't - # check. - it "should be able to sort graphs with duplicate edges" do - one = Puppet::Relationship.new(:a, :b) - @graph.add_edge(one) - two = Puppet::Relationship.new(:a, :b) - @graph.add_edge(two) - proc { @graph.topsort }.should_not raise_error - end - end - - describe "when writing dot files" do - before do - @graph = Puppet::SimpleGraph.new - @name = :test - @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") - end - - it "should only write when graphing is enabled" do - File.expects(:open).with(@file).never - Puppet[:graph] = false - @graph.write_graph(@name) - end - - it "should write a dot file based on the passed name" do - File.expects(:open).with(@file, "w").yields(stub("file", :puts => nil)) - @graph.expects(:to_dot).with("name" => @name.to_s.capitalize) - Puppet[:graph] = true - @graph.write_graph(@name) - end - - after do - Puppet.settings.clear - end - end - - describe Puppet::SimpleGraph do - before do - @graph = Puppet::SimpleGraph.new - end - - it "should correctly clear vertices and edges when asked" do - @graph.add_edge("a", "b") - @graph.add_vertex "c" - @graph.clear - @graph.vertices.should be_empty - @graph.edges.should be_empty - end - end - - describe "when matching edges" do - before do - @graph = Puppet::SimpleGraph.new - @event = Puppet::Transaction::Event.new(:name => :yay, :resource => "a") - @none = Puppet::Transaction::Event.new(:name => :NONE, :resource => "a") - - @edges = {} - @edges["a/b"] = Puppet::Relationship.new("a", "b", {:event => :yay, :callback => :refresh}) - @edges["a/c"] = Puppet::Relationship.new("a", "c", {:event => :yay, :callback => :refresh}) - @graph.add_edge(@edges["a/b"]) - end - - it "should match edges whose source matches the source of the event" do - @graph.matching_edges(@event).should == [@edges["a/b"]] - end - - it "should match always match nothing when the event is :NONE" do - @graph.matching_edges(@none).should be_empty - end - - it "should match multiple edges" do - @graph.add_edge(@edges["a/c"]) - edges = @graph.matching_edges(@event) - edges.should be_include(@edges["a/b"]) - edges.should be_include(@edges["a/c"]) - end - end - - describe "when determining dependencies" do - before do - @graph = Puppet::SimpleGraph.new - - @graph.add_edge("a", "b") - @graph.add_edge("a", "c") - @graph.add_edge("b", "d") - end - - it "should find all dependents when they are on multiple levels" do - @graph.dependents("a").sort.should == %w{b c d}.sort - end - - it "should find single dependents" do - @graph.dependents("b").sort.should == %w{d}.sort - end - - it "should return an empty array when there are no dependents" do - @graph.dependents("c").sort.should == [].sort - end - - it "should find all dependencies when they are on multiple levels" do - @graph.dependencies("d").sort.should == %w{a b} - end - - it "should find single dependencies" do - @graph.dependencies("c").sort.should == %w{a} - end - - it "should return an empty array when there are no dependencies" do - @graph.dependencies("a").sort.should == [] - end - end - - require 'puppet/util/graph' - - class Container - include Puppet::Util::Graph - include Enumerable - attr_accessor :name - def each - @children.each do |c| yield c end - end - - def initialize(name, ary) - @name = name - @children = ary - end - - def push(*ary) - ary.each { |c| @children.push(c)} - end - - def to_s - @name - end - end - - describe "when splicing the graph" do - def container_graph - @one = Container.new("one", %w{a b}) - @two = Container.new("two", ["c", "d"]) - @three = Container.new("three", ["i", "j"]) - @middle = Container.new("middle", ["e", "f", @two]) - @top = Container.new("top", ["g", "h", @middle, @one, @three]) - @empty = Container.new("empty", []) - - @stage = Puppet::Type.type(:stage).new(:name => "foo") - - @contgraph = @top.to_graph - - # We have to add the container to the main graph, else it won't - # be spliced in the dependency graph. - @contgraph.add_vertex(@empty) - end - - def dependency_graph - @depgraph = Puppet::SimpleGraph.new - @contgraph.vertices.each do |v| - @depgraph.add_vertex(v) - end - - # We have to specify a relationship to our empty container, else it - # never makes it into the dep graph in the first place. - {@one => @two, "f" => "c", "h" => @middle, "c" => @empty}.each do |source, target| - @depgraph.add_edge(source, target, :callback => :refresh) - end - end - - def splice - @depgraph.splice!(@contgraph, Container) - end - - before do - container_graph - dependency_graph - splice - end - - # This is the real heart of splicing -- replacing all containers in - # our relationship and exploding their relationships so that each - # relationship to a container gets copied to all of its children. - it "should remove all Container objects from the dependency graph" do - @depgraph.vertices.find_all { |v| v.is_a?(Container) }.should be_empty - end - - # This is a bit hideous, but required to make stages work with relationships - they're - # the top of the graph. - it "should remove all Stage resources from the dependency graph" do - @depgraph.vertices.find_all { |v| v.is_a?(Puppet::Type.type(:stage)) }.should be_empty - end - - it "should add container relationships to contained objects" do - @contgraph.leaves(@middle).each do |leaf| - @depgraph.should be_edge("h", leaf) - end - end - - it "should explode container-to-container relationships, making edges between all respective contained objects" do - @one.each do |oobj| - @two.each do |tobj| - @depgraph.should be_edge(oobj, tobj) - end - end - end - - it "should no longer contain anything but the non-container objects" do - @depgraph.vertices.find_all { |v| ! v.is_a?(String) }.should be_empty - end - - it "should copy labels" do - @depgraph.edges.each do |edge| - edge.label.should == {:callback => :refresh} - end - end - - it "should not add labels to edges that have none" do - @depgraph.add_edge(@two, @three) - splice - @depgraph.edge_label("c", "i").should == {} - end - - it "should copy labels over edges that have none" do - @depgraph.add_edge("c", @three, {:callback => :refresh}) - splice - # And make sure the label got copied. - @depgraph.edge_label("c", "i").should == {:callback => :refresh} - end - - it "should not replace a label with a nil label" do - # Lastly, add some new label-less edges and make sure the label stays. - @depgraph.add_edge(@middle, @three) - @depgraph.add_edge("c", @three, {:callback => :refresh}) - splice - @depgraph.edge_label("c", "i").should == {:callback => :refresh} - end - - it "should copy labels to all created edges" do - @depgraph.add_edge(@middle, @three) - @depgraph.add_edge("c", @three, {:callback => :refresh}) - splice - @three.each do |child| - edge = Puppet::Relationship.new("c", child) - @depgraph.should be_edge(edge.source, edge.target) - @depgraph.edge_label(edge.source, edge.target).should == {:callback => :refresh} - end - end - end -end diff --git a/spec/unit/ssl/base_spec.rb b/spec/unit/ssl/base_spec.rb new file mode 100755 index 000000000..062ea3315 --- /dev/null +++ b/spec/unit/ssl/base_spec.rb @@ -0,0 +1,43 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/ssl/certificate' + +class TestCertificate < Puppet::SSL::Base; end + +describe Puppet::SSL::Certificate do + before :each do + @base = TestCertificate.new("name") + end + + describe "when fingerprinting content" do + before :each do + @cert = stub 'cert', :to_der => "DER" + @base.stubs(:content).returns(@cert) + OpenSSL::Digest.stubs(:constants).returns ["MD5", "DIGEST"] + @digest = stub_everything + OpenSSL::Digest.stubs(:const_get).returns @digest + end + + it "should digest the certificate DER value and return a ':' seperated nibblet string" do + @cert.expects(:to_der).returns("DER") + @digest.expects(:hexdigest).with("DER").returns "digest" + + @base.fingerprint.should == "DI:GE:ST" + end + + it "should raise an error if the digest algorithm is not defined" do + OpenSSL::Digest.expects(:constants).returns [] + + lambda { @base.fingerprint }.should raise_error + end + + it "should use the given digest algorithm" do + OpenSSL::Digest.stubs(:const_get).with("DIGEST").returns @digest + @digest.expects(:hexdigest).with("DER").returns "digest" + + @base.fingerprint(:digest).should == "DI:GE:ST" + end + end +end \ No newline at end of file diff --git a/spec/unit/ssl/base_spec_spec.rb b/spec/unit/ssl/base_spec_spec.rb deleted file mode 100755 index 062ea3315..000000000 --- a/spec/unit/ssl/base_spec_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/ssl/certificate' - -class TestCertificate < Puppet::SSL::Base; end - -describe Puppet::SSL::Certificate do - before :each do - @base = TestCertificate.new("name") - end - - describe "when fingerprinting content" do - before :each do - @cert = stub 'cert', :to_der => "DER" - @base.stubs(:content).returns(@cert) - OpenSSL::Digest.stubs(:constants).returns ["MD5", "DIGEST"] - @digest = stub_everything - OpenSSL::Digest.stubs(:const_get).returns @digest - end - - it "should digest the certificate DER value and return a ':' seperated nibblet string" do - @cert.expects(:to_der).returns("DER") - @digest.expects(:hexdigest).with("DER").returns "digest" - - @base.fingerprint.should == "DI:GE:ST" - end - - it "should raise an error if the digest algorithm is not defined" do - OpenSSL::Digest.expects(:constants).returns [] - - lambda { @base.fingerprint }.should raise_error - end - - it "should use the given digest algorithm" do - OpenSSL::Digest.stubs(:const_get).with("DIGEST").returns @digest - @digest.expects(:hexdigest).with("DER").returns "digest" - - @base.fingerprint(:digest).should == "DI:GE:ST" - end - end -end \ No newline at end of file diff --git a/spec/unit/ssl/certificate_authority/interface_spec.rb b/spec/unit/ssl/certificate_authority/interface_spec.rb new file mode 100755 index 000000000..f3b9107c5 --- /dev/null +++ b/spec/unit/ssl/certificate_authority/interface_spec.rb @@ -0,0 +1,333 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/ssl/certificate_authority' + +describe "a normal interface method", :shared => true do + it "should call the method on the CA for each host specified if an array was provided" do + @ca.expects(@method).with("host1") + @ca.expects(@method).with("host2") + + @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => %w{host1 host2}) + + @applier.apply(@ca) + end + + it "should call the method on the CA for all existing certificates if :all was provided" do + @ca.expects(:list).returns %w{host1 host2} + + @ca.expects(@method).with("host1") + @ca.expects(@method).with("host2") + + @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => :all) + + @applier.apply(@ca) + end +end + +describe Puppet::SSL::CertificateAuthority::Interface do + before do + @class = Puppet::SSL::CertificateAuthority::Interface + end + describe "when initializing" do + it "should set its method using its settor" do + @class.any_instance.expects(:method=).with(:generate) + @class.new(:generate, :to => :all) + end + + it "should set its subjects using the settor" do + @class.any_instance.expects(:subjects=).with(:all) + @class.new(:generate, :to => :all) + end + + it "should set the digest if given" do + interface = @class.new(:generate, :to => :all, :digest => :digest) + interface.digest.should == :digest + end + + it "should set the digest to md5 if none given" do + interface = @class.new(:generate, :to => :all) + interface.digest.should == :MD5 + end + end + + describe "when setting the method" do + it "should set the method" do + @class.new(:generate, :to => :all).method.should == :generate + end + + it "should fail if the method isn't a member of the INTERFACE_METHODS array" do + Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.expects(:include?).with(:thing).returns false + + lambda { @class.new(:thing, :to => :all) }.should raise_error(ArgumentError) + end + end + + describe "when setting the subjects" do + it "should set the subjects" do + @class.new(:generate, :to => :all).subjects.should == :all + end + + it "should fail if the subjects setting isn't :all or an array" do + lambda { @class.new(:generate, "other") }.should raise_error(ArgumentError) + end + end + + it "should have a method for triggering the application" do + @class.new(:generate, :to => :all).should respond_to(:apply) + end + + describe "when applying" do + before do + # We use a real object here, because :verify can't be stubbed, apparently. + @ca = Object.new + end + + it "should raise InterfaceErrors" do + @applier = @class.new(:revoke, :to => :all) + + @ca.expects(:list).raises Puppet::SSL::CertificateAuthority::Interface::InterfaceError + + lambda { @applier.apply(@ca) }.should raise_error(Puppet::SSL::CertificateAuthority::Interface::InterfaceError) + end + + it "should log non-Interface failures rather than failing" do + @applier = @class.new(:revoke, :to => :all) + + @ca.expects(:list).raises ArgumentError + + Puppet.expects(:err) + + lambda { @applier.apply(@ca) }.should_not raise_error + end + + describe "with an empty array specified and the method is not list" do + it "should fail" do + @applier = @class.new(:sign, :to => []) + lambda { @applier.apply(@ca) }.should raise_error(ArgumentError) + end + end + + describe ":generate" do + it "should fail if :all was specified" do + @applier = @class.new(:generate, :to => :all) + lambda { @applier.apply(@ca) }.should raise_error(ArgumentError) + end + + it "should call :generate on the CA for each host specified" do + @applier = @class.new(:generate, :to => %w{host1 host2}) + + @ca.expects(:generate).with("host1") + @ca.expects(:generate).with("host2") + + @applier.apply(@ca) + end + end + + describe ":verify" do + before { @method = :verify } + #it_should_behave_like "a normal interface method" + + it "should call the method on the CA for each host specified if an array was provided" do + # LAK:NOTE Mocha apparently doesn't allow you to mock :verify, but I'm confident this works in real life. + end + + it "should call the method on the CA for all existing certificates if :all was provided" do + # LAK:NOTE Mocha apparently doesn't allow you to mock :verify, but I'm confident this works in real life. + end + end + + describe ":destroy" do + before { @method = :destroy } + it_should_behave_like "a normal interface method" + end + + describe ":revoke" do + before { @method = :revoke } + it_should_behave_like "a normal interface method" + end + + describe ":sign" do + describe "and an array of names was provided" do + before do + @applier = @class.new(:sign, :to => %w{host1 host2}) + end + + it "should sign the specified waiting certificate requests" do + @ca.expects(:sign).with("host1") + @ca.expects(:sign).with("host2") + + @applier.apply(@ca) + end + end + + describe "and :all was provided" do + it "should sign all waiting certificate requests" do + @ca.stubs(:waiting?).returns(%w{cert1 cert2}) + + @ca.expects(:sign).with("cert1") + @ca.expects(:sign).with("cert2") + + @applier = @class.new(:sign, :to => :all) + @applier.apply(@ca) + end + + it "should fail if there are no waiting certificate requests" do + @ca.stubs(:waiting?).returns([]) + + @applier = @class.new(:sign, :to => :all) + lambda { @applier.apply(@ca) }.should raise_error(Puppet::SSL::CertificateAuthority::Interface::InterfaceError) + end + end + end + + describe ":list" do + describe "and an empty array was provided" do + it "should print a string containing all certificate requests" do + @ca.expects(:waiting?).returns %w{host1 host2} + @ca.stubs(:verify) + + @applier = @class.new(:list, :to => []) + + @applier.expects(:puts).with "host1\nhost2" + + @applier.apply(@ca) + end + end + + describe "and :all was provided" do + it "should print a string containing all certificate requests and certificates" do + @ca.expects(:waiting?).returns %w{host1 host2} + @ca.expects(:list).returns %w{host3 host4} + @ca.stubs(:verify) + @ca.stubs(:fingerprint).returns "fingerprint" + @ca.expects(:verify).with("host3").raises(Puppet::SSL::CertificateAuthority::CertificateVerificationError.new(23), "certificate revoked") + + @applier = @class.new(:list, :to => :all) + + @applier.expects(:puts).with "host1 (fingerprint)" + @applier.expects(:puts).with "host2 (fingerprint)" + @applier.expects(:puts).with "- host3 (fingerprint) (certificate revoked)" + @applier.expects(:puts).with "+ host4 (fingerprint)" + + @applier.apply(@ca) + end + end + + describe "and :signed was provided" do + it "should print a string containing all signed certificate requests and certificates" do + @ca.expects(:list).returns %w{host1 host2} + + @applier = @class.new(:list, :to => :signed) + + @applier.apply(@ca) + end + end + + describe "and an array of names was provided" do + it "should print a string of all named hosts that have a waiting request" do + @ca.expects(:waiting?).returns %w{host1 host2} + @ca.expects(:list).returns %w{host3 host4} + @ca.stubs(:fingerprint).returns "fingerprint" + @ca.stubs(:verify) + + @applier = @class.new(:list, :to => %w{host1 host2 host3 host4}) + + @applier.expects(:puts).with "host1 (fingerprint)" + @applier.expects(:puts).with "host2 (fingerprint)" + @applier.expects(:puts).with "+ host3 (fingerprint)" + @applier.expects(:puts).with "+ host4 (fingerprint)" + + @applier.apply(@ca) + end + end + end + + describe ":print" do + describe "and :all was provided" do + it "should print all certificates" do + @ca.expects(:list).returns %w{host1 host2} + + @applier = @class.new(:print, :to => :all) + + @ca.expects(:print).with("host1").returns "h1" + @applier.expects(:puts).with "h1" + + @ca.expects(:print).with("host2").returns "h2" + @applier.expects(:puts).with "h2" + + @applier.apply(@ca) + end + end + + describe "and an array of names was provided" do + it "should print each named certificate if found" do + @applier = @class.new(:print, :to => %w{host1 host2}) + + @ca.expects(:print).with("host1").returns "h1" + @applier.expects(:puts).with "h1" + + @ca.expects(:print).with("host2").returns "h2" + @applier.expects(:puts).with "h2" + + @applier.apply(@ca) + end + + it "should log any named but not found certificates" do + @applier = @class.new(:print, :to => %w{host1 host2}) + + @ca.expects(:print).with("host1").returns "h1" + @applier.expects(:puts).with "h1" + + @ca.expects(:print).with("host2").returns nil + Puppet.expects(:err).with { |msg| msg.include?("host2") } + + @applier.apply(@ca) + end + end + end + + describe ":fingerprint" do + it "should fingerprint with the set digest algorithm" do + @applier = @class.new(:fingerprint, :to => %w{host1}, :digest => :digest) + + @ca.expects(:fingerprint).with("host1", :digest).returns "fingerprint1" + @applier.expects(:puts).with "host1 fingerprint1" + + @applier.apply(@ca) + end + + describe "and :all was provided" do + it "should fingerprint all certificates (including waiting ones)" do + @ca.expects(:list).returns %w{host1} + @ca.expects(:waiting?).returns %w{host2} + + @applier = @class.new(:fingerprint, :to => :all) + + @ca.expects(:fingerprint).with("host1", :MD5).returns "fingerprint1" + @applier.expects(:puts).with "host1 fingerprint1" + + @ca.expects(:fingerprint).with("host2", :MD5).returns "fingerprint2" + @applier.expects(:puts).with "host2 fingerprint2" + + @applier.apply(@ca) + end + end + + describe "and an array of names was provided" do + it "should print each named certificate if found" do + @applier = @class.new(:fingerprint, :to => %w{host1 host2}) + + @ca.expects(:fingerprint).with("host1", :MD5).returns "fingerprint1" + @applier.expects(:puts).with "host1 fingerprint1" + + @ca.expects(:fingerprint).with("host2", :MD5).returns "fingerprint2" + @applier.expects(:puts).with "host2 fingerprint2" + + @applier.apply(@ca) + end + end + end + end +end diff --git a/spec/unit/ssl/certificate_authority/interface_spec_spec.rb b/spec/unit/ssl/certificate_authority/interface_spec_spec.rb deleted file mode 100755 index f3b9107c5..000000000 --- a/spec/unit/ssl/certificate_authority/interface_spec_spec.rb +++ /dev/null @@ -1,333 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/ssl/certificate_authority' - -describe "a normal interface method", :shared => true do - it "should call the method on the CA for each host specified if an array was provided" do - @ca.expects(@method).with("host1") - @ca.expects(@method).with("host2") - - @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => %w{host1 host2}) - - @applier.apply(@ca) - end - - it "should call the method on the CA for all existing certificates if :all was provided" do - @ca.expects(:list).returns %w{host1 host2} - - @ca.expects(@method).with("host1") - @ca.expects(@method).with("host2") - - @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => :all) - - @applier.apply(@ca) - end -end - -describe Puppet::SSL::CertificateAuthority::Interface do - before do - @class = Puppet::SSL::CertificateAuthority::Interface - end - describe "when initializing" do - it "should set its method using its settor" do - @class.any_instance.expects(:method=).with(:generate) - @class.new(:generate, :to => :all) - end - - it "should set its subjects using the settor" do - @class.any_instance.expects(:subjects=).with(:all) - @class.new(:generate, :to => :all) - end - - it "should set the digest if given" do - interface = @class.new(:generate, :to => :all, :digest => :digest) - interface.digest.should == :digest - end - - it "should set the digest to md5 if none given" do - interface = @class.new(:generate, :to => :all) - interface.digest.should == :MD5 - end - end - - describe "when setting the method" do - it "should set the method" do - @class.new(:generate, :to => :all).method.should == :generate - end - - it "should fail if the method isn't a member of the INTERFACE_METHODS array" do - Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.expects(:include?).with(:thing).returns false - - lambda { @class.new(:thing, :to => :all) }.should raise_error(ArgumentError) - end - end - - describe "when setting the subjects" do - it "should set the subjects" do - @class.new(:generate, :to => :all).subjects.should == :all - end - - it "should fail if the subjects setting isn't :all or an array" do - lambda { @class.new(:generate, "other") }.should raise_error(ArgumentError) - end - end - - it "should have a method for triggering the application" do - @class.new(:generate, :to => :all).should respond_to(:apply) - end - - describe "when applying" do - before do - # We use a real object here, because :verify can't be stubbed, apparently. - @ca = Object.new - end - - it "should raise InterfaceErrors" do - @applier = @class.new(:revoke, :to => :all) - - @ca.expects(:list).raises Puppet::SSL::CertificateAuthority::Interface::InterfaceError - - lambda { @applier.apply(@ca) }.should raise_error(Puppet::SSL::CertificateAuthority::Interface::InterfaceError) - end - - it "should log non-Interface failures rather than failing" do - @applier = @class.new(:revoke, :to => :all) - - @ca.expects(:list).raises ArgumentError - - Puppet.expects(:err) - - lambda { @applier.apply(@ca) }.should_not raise_error - end - - describe "with an empty array specified and the method is not list" do - it "should fail" do - @applier = @class.new(:sign, :to => []) - lambda { @applier.apply(@ca) }.should raise_error(ArgumentError) - end - end - - describe ":generate" do - it "should fail if :all was specified" do - @applier = @class.new(:generate, :to => :all) - lambda { @applier.apply(@ca) }.should raise_error(ArgumentError) - end - - it "should call :generate on the CA for each host specified" do - @applier = @class.new(:generate, :to => %w{host1 host2}) - - @ca.expects(:generate).with("host1") - @ca.expects(:generate).with("host2") - - @applier.apply(@ca) - end - end - - describe ":verify" do - before { @method = :verify } - #it_should_behave_like "a normal interface method" - - it "should call the method on the CA for each host specified if an array was provided" do - # LAK:NOTE Mocha apparently doesn't allow you to mock :verify, but I'm confident this works in real life. - end - - it "should call the method on the CA for all existing certificates if :all was provided" do - # LAK:NOTE Mocha apparently doesn't allow you to mock :verify, but I'm confident this works in real life. - end - end - - describe ":destroy" do - before { @method = :destroy } - it_should_behave_like "a normal interface method" - end - - describe ":revoke" do - before { @method = :revoke } - it_should_behave_like "a normal interface method" - end - - describe ":sign" do - describe "and an array of names was provided" do - before do - @applier = @class.new(:sign, :to => %w{host1 host2}) - end - - it "should sign the specified waiting certificate requests" do - @ca.expects(:sign).with("host1") - @ca.expects(:sign).with("host2") - - @applier.apply(@ca) - end - end - - describe "and :all was provided" do - it "should sign all waiting certificate requests" do - @ca.stubs(:waiting?).returns(%w{cert1 cert2}) - - @ca.expects(:sign).with("cert1") - @ca.expects(:sign).with("cert2") - - @applier = @class.new(:sign, :to => :all) - @applier.apply(@ca) - end - - it "should fail if there are no waiting certificate requests" do - @ca.stubs(:waiting?).returns([]) - - @applier = @class.new(:sign, :to => :all) - lambda { @applier.apply(@ca) }.should raise_error(Puppet::SSL::CertificateAuthority::Interface::InterfaceError) - end - end - end - - describe ":list" do - describe "and an empty array was provided" do - it "should print a string containing all certificate requests" do - @ca.expects(:waiting?).returns %w{host1 host2} - @ca.stubs(:verify) - - @applier = @class.new(:list, :to => []) - - @applier.expects(:puts).with "host1\nhost2" - - @applier.apply(@ca) - end - end - - describe "and :all was provided" do - it "should print a string containing all certificate requests and certificates" do - @ca.expects(:waiting?).returns %w{host1 host2} - @ca.expects(:list).returns %w{host3 host4} - @ca.stubs(:verify) - @ca.stubs(:fingerprint).returns "fingerprint" - @ca.expects(:verify).with("host3").raises(Puppet::SSL::CertificateAuthority::CertificateVerificationError.new(23), "certificate revoked") - - @applier = @class.new(:list, :to => :all) - - @applier.expects(:puts).with "host1 (fingerprint)" - @applier.expects(:puts).with "host2 (fingerprint)" - @applier.expects(:puts).with "- host3 (fingerprint) (certificate revoked)" - @applier.expects(:puts).with "+ host4 (fingerprint)" - - @applier.apply(@ca) - end - end - - describe "and :signed was provided" do - it "should print a string containing all signed certificate requests and certificates" do - @ca.expects(:list).returns %w{host1 host2} - - @applier = @class.new(:list, :to => :signed) - - @applier.apply(@ca) - end - end - - describe "and an array of names was provided" do - it "should print a string of all named hosts that have a waiting request" do - @ca.expects(:waiting?).returns %w{host1 host2} - @ca.expects(:list).returns %w{host3 host4} - @ca.stubs(:fingerprint).returns "fingerprint" - @ca.stubs(:verify) - - @applier = @class.new(:list, :to => %w{host1 host2 host3 host4}) - - @applier.expects(:puts).with "host1 (fingerprint)" - @applier.expects(:puts).with "host2 (fingerprint)" - @applier.expects(:puts).with "+ host3 (fingerprint)" - @applier.expects(:puts).with "+ host4 (fingerprint)" - - @applier.apply(@ca) - end - end - end - - describe ":print" do - describe "and :all was provided" do - it "should print all certificates" do - @ca.expects(:list).returns %w{host1 host2} - - @applier = @class.new(:print, :to => :all) - - @ca.expects(:print).with("host1").returns "h1" - @applier.expects(:puts).with "h1" - - @ca.expects(:print).with("host2").returns "h2" - @applier.expects(:puts).with "h2" - - @applier.apply(@ca) - end - end - - describe "and an array of names was provided" do - it "should print each named certificate if found" do - @applier = @class.new(:print, :to => %w{host1 host2}) - - @ca.expects(:print).with("host1").returns "h1" - @applier.expects(:puts).with "h1" - - @ca.expects(:print).with("host2").returns "h2" - @applier.expects(:puts).with "h2" - - @applier.apply(@ca) - end - - it "should log any named but not found certificates" do - @applier = @class.new(:print, :to => %w{host1 host2}) - - @ca.expects(:print).with("host1").returns "h1" - @applier.expects(:puts).with "h1" - - @ca.expects(:print).with("host2").returns nil - Puppet.expects(:err).with { |msg| msg.include?("host2") } - - @applier.apply(@ca) - end - end - end - - describe ":fingerprint" do - it "should fingerprint with the set digest algorithm" do - @applier = @class.new(:fingerprint, :to => %w{host1}, :digest => :digest) - - @ca.expects(:fingerprint).with("host1", :digest).returns "fingerprint1" - @applier.expects(:puts).with "host1 fingerprint1" - - @applier.apply(@ca) - end - - describe "and :all was provided" do - it "should fingerprint all certificates (including waiting ones)" do - @ca.expects(:list).returns %w{host1} - @ca.expects(:waiting?).returns %w{host2} - - @applier = @class.new(:fingerprint, :to => :all) - - @ca.expects(:fingerprint).with("host1", :MD5).returns "fingerprint1" - @applier.expects(:puts).with "host1 fingerprint1" - - @ca.expects(:fingerprint).with("host2", :MD5).returns "fingerprint2" - @applier.expects(:puts).with "host2 fingerprint2" - - @applier.apply(@ca) - end - end - - describe "and an array of names was provided" do - it "should print each named certificate if found" do - @applier = @class.new(:fingerprint, :to => %w{host1 host2}) - - @ca.expects(:fingerprint).with("host1", :MD5).returns "fingerprint1" - @applier.expects(:puts).with "host1 fingerprint1" - - @ca.expects(:fingerprint).with("host2", :MD5).returns "fingerprint2" - @applier.expects(:puts).with "host2 fingerprint2" - - @applier.apply(@ca) - end - end - end - end -end diff --git a/spec/unit/ssl/certificate_authority_spec.rb b/spec/unit/ssl/certificate_authority_spec.rb new file mode 100755 index 000000000..3b1b9486f --- /dev/null +++ b/spec/unit/ssl/certificate_authority_spec.rb @@ -0,0 +1,772 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/ssl/certificate_authority' + +describe Puppet::SSL::CertificateAuthority do + after do + Puppet::Util::Cacher.expire + Puppet.settings.clearused + end + + def stub_ca_host + @key = mock 'key' + @key.stubs(:content).returns "cakey" + @cacert = mock 'certificate' + @cacert.stubs(:content).returns "cacertificate" + + @host = stub 'ssl_host', :key => @key, :certificate => @cacert, :name => Puppet::SSL::Host.ca_name + end + + it "should have a class method for returning a singleton instance" do + Puppet::SSL::CertificateAuthority.should respond_to(:instance) + end + + describe "when finding an existing instance" do + describe "and the host is a CA host and the mode is master" do + before do + Puppet.settings.stubs(:value).with(:ca).returns true + Puppet.mode.stubs(:master?).returns true + + @ca = mock('ca') + Puppet::SSL::CertificateAuthority.stubs(:new).returns @ca + end + + it "should return an instance" do + Puppet::SSL::CertificateAuthority.instance.should equal(@ca) + end + + it "should always return the same instance" do + Puppet::SSL::CertificateAuthority.instance.should equal(Puppet::SSL::CertificateAuthority.instance) + end + end + + describe "and the host is not a CA host" do + it "should return nil" do + Puppet.settings.stubs(:value).with(:ca).returns false + Puppet.mode.stubs(:master?).returns true + + ca = mock('ca') + Puppet::SSL::CertificateAuthority.expects(:new).never + Puppet::SSL::CertificateAuthority.instance.should be_nil + end + end + + describe "and the mode is not master" do + it "should return nil" do + Puppet.settings.stubs(:value).with(:ca).returns true + Puppet.mode.stubs(:master?).returns false + + ca = mock('ca') + Puppet::SSL::CertificateAuthority.expects(:new).never + Puppet::SSL::CertificateAuthority.instance.should be_nil + end + end + end + + describe "when initializing" do + before do + Puppet.settings.stubs(:use) + Puppet.settings.stubs(:value).returns "ca_testing" + + Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) + end + + it "should always set its name to the value of :certname" do + Puppet.settings.expects(:value).with(:certname).returns "ca_testing" + + Puppet::SSL::CertificateAuthority.new.name.should == "ca_testing" + end + + it "should create an SSL::Host instance whose name is the 'ca_name'" do + Puppet::SSL::Host.expects(:ca_name).returns "caname" + + host = stub 'host' + Puppet::SSL::Host.expects(:new).with("caname").returns host + + Puppet::SSL::CertificateAuthority.new + end + + it "should use the :main, :ca, and :ssl settings sections" do + Puppet.settings.expects(:use).with(:main, :ssl, :ca) + Puppet::SSL::CertificateAuthority.new + end + + it "should create an inventory instance" do + Puppet::SSL::Inventory.expects(:new).returns "inventory" + + Puppet::SSL::CertificateAuthority.new.inventory.should == "inventory" + end + + it "should make sure the CA is set up" do + Puppet::SSL::CertificateAuthority.any_instance.expects(:setup) + + Puppet::SSL::CertificateAuthority.new + end + end + + describe "when setting itself up" do + it "should generate the CA certificate if it does not have one" do + Puppet.settings.stubs :use + + host = stub 'host' + Puppet::SSL::Host.stubs(:new).returns host + + host.expects(:certificate).returns nil + + Puppet::SSL::CertificateAuthority.any_instance.expects(:generate_ca_certificate) + Puppet::SSL::CertificateAuthority.new + end + end + + describe "when retrieving the certificate revocation list" do + before do + Puppet.settings.stubs(:use) + Puppet.settings.stubs(:value).returns "ca_testing" + Puppet.settings.stubs(:value).with(:cacrl).returns "/my/crl" + + cert = stub("certificate", :content => "real_cert") + key = stub("key", :content => "real_key") + @host = stub 'host', :certificate => cert, :name => "hostname", :key => key + + Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) + @ca = Puppet::SSL::CertificateAuthority.new + + @ca.stubs(:host).returns @host + end + + it "should return any found CRL instance" do + crl = mock 'crl' + Puppet::SSL::CertificateRevocationList.expects(:find).returns crl + @ca.crl.should equal(crl) + end + + it "should create, generate, and save a new CRL instance of no CRL can be found" do + crl = mock 'crl' + Puppet::SSL::CertificateRevocationList.expects(:find).returns nil + + Puppet::SSL::CertificateRevocationList.expects(:new).returns crl + + crl.expects(:generate).with(@ca.host.certificate.content, @ca.host.key.content) + crl.expects(:save) + + @ca.crl.should equal(crl) + end + end + + describe "when generating a self-signed CA certificate" do + before do + Puppet.settings.stubs(:use) + Puppet.settings.stubs(:value).returns "ca_testing" + + Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) + Puppet::SSL::CertificateAuthority.any_instance.stubs(:crl) + @ca = Puppet::SSL::CertificateAuthority.new + + @host = stub 'host', :key => mock("key"), :name => "hostname", :certificate => mock('certificate') + + Puppet::SSL::CertificateRequest.any_instance.stubs(:generate) + + @ca.stubs(:host).returns @host + end + + it "should create and store a password at :capass" do + Puppet.settings.expects(:value).with(:capass).returns "/path/to/pass" + + FileTest.expects(:exist?).with("/path/to/pass").returns false + + fh = mock 'filehandle' + Puppet.settings.expects(:write).with(:capass).yields fh + + fh.expects(:print).with { |s| s.length > 18 } + + @ca.stubs(:sign) + + @ca.generate_ca_certificate + end + + it "should generate a key if one does not exist" do + @ca.stubs :generate_password + @ca.stubs :sign + + @ca.host.expects(:key).returns nil + @ca.host.expects(:generate_key) + + @ca.generate_ca_certificate + end + + it "should create and sign a self-signed cert using the CA name" do + request = mock 'request' + Puppet::SSL::CertificateRequest.expects(:new).with(@ca.host.name).returns request + request.expects(:generate).with(@ca.host.key) + + @ca.expects(:sign).with(@host.name, :ca, request) + + @ca.stubs :generate_password + + @ca.generate_ca_certificate + end + + it "should generate its CRL" do + @ca.stubs :generate_password + @ca.stubs :sign + + @ca.host.expects(:key).returns nil + @ca.host.expects(:generate_key) + + @ca.expects(:crl) + + @ca.generate_ca_certificate + end + end + + describe "when signing" do + before do + Puppet.settings.stubs(:use) + + Puppet::SSL::CertificateAuthority.any_instance.stubs(:password?).returns true + + stub_ca_host + + Puppet::SSL::Host.expects(:new).with(Puppet::SSL::Host.ca_name).returns @host + + @ca = Puppet::SSL::CertificateAuthority.new + + @name = "myhost" + @real_cert = stub 'realcert', :sign => nil + @cert = stub 'certificate', :content => @real_cert + + Puppet::SSL::Certificate.stubs(:new).returns @cert + + @cert.stubs(:content=) + @cert.stubs(:save) + + # Stub out the factory + @factory = stub 'factory', :result => "my real cert" + Puppet::SSL::CertificateFactory.stubs(:new).returns @factory + + @request = stub 'request', :content => "myrequest", :name => @name + + # And the inventory + @inventory = stub 'inventory', :add => nil + @ca.stubs(:inventory).returns @inventory + + Puppet::SSL::CertificateRequest.stubs(:destroy) + end + + describe "and calculating the next certificate serial number" do + before do + @path = "/path/to/serial" + Puppet.settings.stubs(:value).with(:serial).returns @path + + @filehandle = stub 'filehandle', :<< => @filehandle + Puppet.settings.stubs(:readwritelock).with(:serial).yields @filehandle + end + + it "should default to 0x1 for the first serial number" do + @ca.next_serial.should == 0x1 + end + + it "should return the current content of the serial file" do + FileTest.stubs(:exist?).with(@path).returns true + File.expects(:read).with(@path).returns "0002" + + @ca.next_serial.should == 2 + end + + it "should write the next serial number to the serial file as hex" do + @filehandle.expects(:<<).with("0002") + + @ca.next_serial + end + + it "should lock the serial file while writing" do + Puppet.settings.expects(:readwritelock).with(:serial) + + @ca.next_serial + end + end + + describe "its own certificate" do + before do + @serial = 10 + @ca.stubs(:next_serial).returns @serial + end + + it "should not look up a certificate request for the host" do + Puppet::SSL::CertificateRequest.expects(:find).never + + @ca.sign(@name, :ca, @request) + end + + it "should use a certificate type of :ca" do + Puppet::SSL::CertificateFactory.expects(:new).with do |*args| + args[0] == :ca + end.returns @factory + @ca.sign(@name, :ca, @request) + end + + it "should pass the provided CSR as the CSR" do + Puppet::SSL::CertificateFactory.expects(:new).with do |*args| + args[1] == "myrequest" + end.returns @factory + @ca.sign(@name, :ca, @request) + end + + it "should use the provided CSR's content as the issuer" do + Puppet::SSL::CertificateFactory.expects(:new).with do |*args| + args[2] == "myrequest" + end.returns @factory + @ca.sign(@name, :ca, @request) + end + + it "should pass the next serial as the serial number" do + Puppet::SSL::CertificateFactory.expects(:new).with do |*args| + args[3] == @serial + end.returns @factory + @ca.sign(@name, :ca, @request) + end + + it "should save the resulting certificate" do + @cert.expects(:save) + + @ca.sign(@name, :ca, @request) + end + end + + describe "another host's certificate" do + before do + @serial = 10 + @ca.stubs(:next_serial).returns @serial + + Puppet::SSL::CertificateRequest.stubs(:find).with(@name).returns @request + @cert.stubs :save + end + + it "should use a certificate type of :server" do + Puppet::SSL::CertificateFactory.expects(:new).with do |*args| + args[0] == :server + end.returns @factory + + @ca.sign(@name) + end + + it "should use look up a CSR for the host in the :ca_file terminus" do + Puppet::SSL::CertificateRequest.expects(:find).with(@name).returns @request + + @ca.sign(@name) + end + + it "should fail if no CSR can be found for the host" do + Puppet::SSL::CertificateRequest.expects(:find).with(@name).returns nil + + lambda { @ca.sign(@name) }.should raise_error(ArgumentError) + end + + it "should use the CA certificate as the issuer" do + Puppet::SSL::CertificateFactory.expects(:new).with do |*args| + args[2] == @cacert.content + end.returns @factory + @ca.sign(@name) + end + + it "should pass the next serial as the serial number" do + Puppet::SSL::CertificateFactory.expects(:new).with do |*args| + args[3] == @serial + end.returns @factory + @ca.sign(@name) + end + + it "should sign the resulting certificate using its real key and a digest" do + digest = mock 'digest' + OpenSSL::Digest::SHA1.expects(:new).returns digest + + key = stub 'key', :content => "real_key" + @ca.host.stubs(:key).returns key + + @cert.content.expects(:sign).with("real_key", digest) + @ca.sign(@name) + end + + it "should save the resulting certificate" do + @cert.expects(:save) + @ca.sign(@name) + end + + it "should remove the host's certificate request" do + Puppet::SSL::CertificateRequest.expects(:destroy).with(@name) + + @ca.sign(@name) + end + end + + it "should create a certificate instance with the content set to the newly signed x509 certificate" do + @serial = 10 + @ca.stubs(:next_serial).returns @serial + + Puppet::SSL::CertificateRequest.stubs(:find).with(@name).returns @request + @cert.stubs :save + Puppet::SSL::Certificate.expects(:new).with(@name).returns @cert + + @ca.sign(@name) + end + + it "should return the certificate instance" do + @ca.stubs(:next_serial).returns @serial + Puppet::SSL::CertificateRequest.stubs(:find).with(@name).returns @request + @cert.stubs :save + @ca.sign(@name).should equal(@cert) + end + + it "should add the certificate to its inventory" do + @ca.stubs(:next_serial).returns @serial + @inventory.expects(:add).with(@cert) + + Puppet::SSL::CertificateRequest.stubs(:find).with(@name).returns @request + @cert.stubs :save + @ca.sign(@name) + end + + it "should have a method for triggering autosigning of available CSRs" do + @ca.should respond_to(:autosign) + end + + describe "when autosigning certificates" do + it "should do nothing if autosign is disabled" do + Puppet.settings.expects(:value).with(:autosign).returns 'false' + + Puppet::SSL::CertificateRequest.expects(:search).never + @ca.autosign + end + + it "should do nothing if no autosign.conf exists" do + Puppet.settings.expects(:value).with(:autosign).returns '/auto/sign' + FileTest.expects(:exist?).with("/auto/sign").returns false + + Puppet::SSL::CertificateRequest.expects(:search).never + @ca.autosign + end + + describe "and autosign is enabled and the autosign.conf file exists" do + before do + Puppet.settings.stubs(:value).with(:autosign).returns '/auto/sign' + FileTest.stubs(:exist?).with("/auto/sign").returns true + File.stubs(:readlines).with("/auto/sign").returns ["one\n", "two\n"] + + Puppet::SSL::CertificateRequest.stubs(:search).returns [] + + @store = stub 'store', :allow => nil + Puppet::Network::AuthStore.stubs(:new).returns @store + end + + describe "when creating the AuthStore instance to verify autosigning" do + it "should create an AuthStore with each line in the configuration file allowed to be autosigned" do + Puppet::Network::AuthStore.expects(:new).returns @store + + @store.expects(:allow).with("one") + @store.expects(:allow).with("two") + + @ca.autosign + end + + it "should reparse the autosign configuration on each call" do + Puppet::Network::AuthStore.expects(:new).times(2).returns @store + + @ca.autosign + @ca.autosign + end + + it "should ignore comments" do + File.stubs(:readlines).with("/auto/sign").returns ["one\n", "#two\n"] + + @store.expects(:allow).with("one") + @ca.autosign + end + + it "should ignore blank lines" do + File.stubs(:readlines).with("/auto/sign").returns ["one\n", "\n"] + + @store.expects(:allow).with("one") + @ca.autosign + end + end + + it "should sign all CSRs whose hostname matches the autosign configuration" do + csr1 = mock 'csr1' + csr2 = mock 'csr2' + Puppet::SSL::CertificateRequest.stubs(:search).returns [csr1, csr2] + end + + it "should not sign CSRs whose hostname does not match the autosign configuration" do + csr1 = mock 'csr1' + csr2 = mock 'csr2' + Puppet::SSL::CertificateRequest.stubs(:search).returns [csr1, csr2] + end + end + end + end + + describe "when managing certificate clients" do + before do + Puppet.settings.stubs(:use) + + Puppet::SSL::CertificateAuthority.any_instance.stubs(:password?).returns true + + stub_ca_host + + Puppet::SSL::Host.expects(:new).returns @host + Puppet::SSL::CertificateAuthority.any_instance.stubs(:host).returns @host + + @cacert = mock 'certificate' + @cacert.stubs(:content).returns "cacertificate" + @ca = Puppet::SSL::CertificateAuthority.new + end + + it "should have a method for acting on the SSL files" do + @ca.should respond_to(:apply) + end + + describe "when applying a method to a set of hosts" do + it "should fail if no subjects have been specified" do + lambda { @ca.apply(:generate) }.should raise_error(ArgumentError) + end + + it "should create an Interface instance with the specified method and the options" do + Puppet::SSL::CertificateAuthority::Interface.expects(:new).with(:generate, :to => :host).returns(stub('applier', :apply => nil)) + @ca.apply(:generate, :to => :host) + end + + it "should apply the Interface with itself as the argument" do + applier = stub('applier') + applier.expects(:apply).with(@ca) + Puppet::SSL::CertificateAuthority::Interface.expects(:new).returns applier + @ca.apply(:generate, :to => :ca_testing) + end + end + + it "should be able to list waiting certificate requests" do + req1 = stub 'req1', :name => "one" + req2 = stub 'req2', :name => "two" + Puppet::SSL::CertificateRequest.expects(:search).with("*").returns [req1, req2] + + @ca.waiting?.should == %w{one two} + end + + it "should delegate removing hosts to the Host class" do + Puppet::SSL::Host.expects(:destroy).with("myhost") + + @ca.destroy("myhost") + end + + it "should be able to verify certificates" do + @ca.should respond_to(:verify) + end + + it "should list certificates as the sorted list of all existing signed certificates" do + cert1 = stub 'cert1', :name => "cert1" + cert2 = stub 'cert2', :name => "cert2" + Puppet::SSL::Certificate.expects(:search).with("*").returns [cert1, cert2] + @ca.list.should == %w{cert1 cert2} + end + + describe "and printing certificates" do + it "should return nil if the certificate cannot be found" do + Puppet::SSL::Certificate.expects(:find).with("myhost").returns nil + @ca.print("myhost").should be_nil + end + + it "should print certificates by calling :to_text on the host's certificate" do + cert1 = stub 'cert1', :name => "cert1", :to_text => "mytext" + Puppet::SSL::Certificate.expects(:find).with("myhost").returns cert1 + @ca.print("myhost").should == "mytext" + end + end + + describe "and fingerprinting certificates" do + before :each do + @cert = stub 'cert', :name => "cert", :fingerprint => "DIGEST" + Puppet::SSL::Certificate.stubs(:find).with("myhost").returns @cert + Puppet::SSL::CertificateRequest.stubs(:find).with("myhost") + end + + it "should raise an error if the certificate or CSR cannot be found" do + Puppet::SSL::Certificate.expects(:find).with("myhost").returns nil + Puppet::SSL::CertificateRequest.expects(:find).with("myhost").returns nil + lambda { @ca.fingerprint("myhost") }.should raise_error + end + + it "should try to find a CSR if no certificate can be found" do + Puppet::SSL::Certificate.expects(:find).with("myhost").returns nil + Puppet::SSL::CertificateRequest.expects(:find).with("myhost").returns @cert + @cert.expects(:fingerprint) + @ca.fingerprint("myhost") + end + + it "should delegate to the certificate fingerprinting" do + @cert.expects(:fingerprint) + @ca.fingerprint("myhost") + end + + it "should propagate the digest algorithm to the certificate fingerprinting system" do + @cert.expects(:fingerprint).with(:digest) + @ca.fingerprint("myhost", :digest) + end + end + + describe "and verifying certificates" do + before do + @store = stub 'store', :verify => true, :add_file => nil, :purpose= => nil, :add_crl => true, :flags= => nil + + OpenSSL::X509::Store.stubs(:new).returns @store + + Puppet.settings.stubs(:value).returns "crtstuff" + + @cert = stub 'cert', :content => "mycert" + Puppet::SSL::Certificate.stubs(:find).returns @cert + + @crl = stub('crl', :content => "mycrl") + + @ca.stubs(:crl).returns @crl + end + + it "should fail if the host's certificate cannot be found" do + Puppet::SSL::Certificate.expects(:find).with("me").returns(nil) + + lambda { @ca.verify("me") }.should raise_error(ArgumentError) + end + + it "should create an SSL Store to verify" do + OpenSSL::X509::Store.expects(:new).returns @store + + @ca.verify("me") + end + + it "should add the CA Certificate to the store" do + Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" + @store.expects(:add_file).with "/ca/cert" + + @ca.verify("me") + end + + it "should add the CRL to the store if the crl is enabled" do + @store.expects(:add_crl).with "mycrl" + + @ca.verify("me") + end + + it "should set the store purpose to OpenSSL::X509::PURPOSE_SSL_CLIENT" do + Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" + @store.expects(:add_file).with "/ca/cert" + + @ca.verify("me") + end + + it "should set the store flags to check the crl" do + @store.expects(:flags=).with OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK + + @ca.verify("me") + end + + it "should use the store to verify the certificate" do + @cert.expects(:content).returns "mycert" + + @store.expects(:verify).with("mycert").returns true + + @ca.verify("me") + end + + it "should fail if the verification returns false" do + @cert.expects(:content).returns "mycert" + + @store.expects(:verify).with("mycert").returns false + + lambda { @ca.verify("me") }.should raise_error + end + end + + describe "and revoking certificates" do + before do + @crl = mock 'crl' + @ca.stubs(:crl).returns @crl + + @ca.stubs(:next_serial).returns 10 + + @real_cert = stub 'real_cert', :serial => 15 + @cert = stub 'cert', :content => @real_cert + Puppet::SSL::Certificate.stubs(:find).returns @cert + + end + + it "should fail if the certificate revocation list is disabled" do + @ca.stubs(:crl).returns false + + lambda { @ca.revoke('ca_testing') }.should raise_error(ArgumentError) + + end + + it "should delegate the revocation to its CRL" do + @ca.crl.expects(:revoke) + + @ca.revoke('host') + end + + it "should get the serial number from the local certificate if it exists" do + @ca.crl.expects(:revoke).with { |serial, key| serial == 15 } + + Puppet::SSL::Certificate.expects(:find).with("host").returns @cert + + @ca.revoke('host') + end + + it "should get the serial number from inventory if no local certificate exists" do + real_cert = stub 'real_cert', :serial => 15 + cert = stub 'cert', :content => real_cert + Puppet::SSL::Certificate.expects(:find).with("host").returns nil + + @ca.inventory.expects(:serial).with("host").returns 16 + + @ca.crl.expects(:revoke).with { |serial, key| serial == 16 } + @ca.revoke('host') + end + end + + it "should be able to generate a complete new SSL host" do + @ca.should respond_to(:generate) + end + + describe "and generating certificates" do + before do + @host = stub 'host', :generate_certificate_request => nil + Puppet::SSL::Host.stubs(:new).returns @host + Puppet::SSL::Certificate.stubs(:find).returns nil + + @ca.stubs(:sign) + end + + it "should fail if a certificate already exists for the host" do + Puppet::SSL::Certificate.expects(:find).with("him").returns "something" + + lambda { @ca.generate("him") }.should raise_error(ArgumentError) + end + + it "should create a new Host instance with the correct name" do + Puppet::SSL::Host.expects(:new).with("him").returns @host + + @ca.generate("him") + end + + it "should use the Host to generate the certificate request" do + @host.expects :generate_certificate_request + + @ca.generate("him") + end + + it "should sign the generated request" do + @ca.expects(:sign).with("him") + + @ca.generate("him") + end + end + end +end diff --git a/spec/unit/ssl/certificate_authority_spec_spec.rb b/spec/unit/ssl/certificate_authority_spec_spec.rb deleted file mode 100755 index 3b1b9486f..000000000 --- a/spec/unit/ssl/certificate_authority_spec_spec.rb +++ /dev/null @@ -1,772 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/ssl/certificate_authority' - -describe Puppet::SSL::CertificateAuthority do - after do - Puppet::Util::Cacher.expire - Puppet.settings.clearused - end - - def stub_ca_host - @key = mock 'key' - @key.stubs(:content).returns "cakey" - @cacert = mock 'certificate' - @cacert.stubs(:content).returns "cacertificate" - - @host = stub 'ssl_host', :key => @key, :certificate => @cacert, :name => Puppet::SSL::Host.ca_name - end - - it "should have a class method for returning a singleton instance" do - Puppet::SSL::CertificateAuthority.should respond_to(:instance) - end - - describe "when finding an existing instance" do - describe "and the host is a CA host and the mode is master" do - before do - Puppet.settings.stubs(:value).with(:ca).returns true - Puppet.mode.stubs(:master?).returns true - - @ca = mock('ca') - Puppet::SSL::CertificateAuthority.stubs(:new).returns @ca - end - - it "should return an instance" do - Puppet::SSL::CertificateAuthority.instance.should equal(@ca) - end - - it "should always return the same instance" do - Puppet::SSL::CertificateAuthority.instance.should equal(Puppet::SSL::CertificateAuthority.instance) - end - end - - describe "and the host is not a CA host" do - it "should return nil" do - Puppet.settings.stubs(:value).with(:ca).returns false - Puppet.mode.stubs(:master?).returns true - - ca = mock('ca') - Puppet::SSL::CertificateAuthority.expects(:new).never - Puppet::SSL::CertificateAuthority.instance.should be_nil - end - end - - describe "and the mode is not master" do - it "should return nil" do - Puppet.settings.stubs(:value).with(:ca).returns true - Puppet.mode.stubs(:master?).returns false - - ca = mock('ca') - Puppet::SSL::CertificateAuthority.expects(:new).never - Puppet::SSL::CertificateAuthority.instance.should be_nil - end - end - end - - describe "when initializing" do - before do - Puppet.settings.stubs(:use) - Puppet.settings.stubs(:value).returns "ca_testing" - - Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) - end - - it "should always set its name to the value of :certname" do - Puppet.settings.expects(:value).with(:certname).returns "ca_testing" - - Puppet::SSL::CertificateAuthority.new.name.should == "ca_testing" - end - - it "should create an SSL::Host instance whose name is the 'ca_name'" do - Puppet::SSL::Host.expects(:ca_name).returns "caname" - - host = stub 'host' - Puppet::SSL::Host.expects(:new).with("caname").returns host - - Puppet::SSL::CertificateAuthority.new - end - - it "should use the :main, :ca, and :ssl settings sections" do - Puppet.settings.expects(:use).with(:main, :ssl, :ca) - Puppet::SSL::CertificateAuthority.new - end - - it "should create an inventory instance" do - Puppet::SSL::Inventory.expects(:new).returns "inventory" - - Puppet::SSL::CertificateAuthority.new.inventory.should == "inventory" - end - - it "should make sure the CA is set up" do - Puppet::SSL::CertificateAuthority.any_instance.expects(:setup) - - Puppet::SSL::CertificateAuthority.new - end - end - - describe "when setting itself up" do - it "should generate the CA certificate if it does not have one" do - Puppet.settings.stubs :use - - host = stub 'host' - Puppet::SSL::Host.stubs(:new).returns host - - host.expects(:certificate).returns nil - - Puppet::SSL::CertificateAuthority.any_instance.expects(:generate_ca_certificate) - Puppet::SSL::CertificateAuthority.new - end - end - - describe "when retrieving the certificate revocation list" do - before do - Puppet.settings.stubs(:use) - Puppet.settings.stubs(:value).returns "ca_testing" - Puppet.settings.stubs(:value).with(:cacrl).returns "/my/crl" - - cert = stub("certificate", :content => "real_cert") - key = stub("key", :content => "real_key") - @host = stub 'host', :certificate => cert, :name => "hostname", :key => key - - Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) - @ca = Puppet::SSL::CertificateAuthority.new - - @ca.stubs(:host).returns @host - end - - it "should return any found CRL instance" do - crl = mock 'crl' - Puppet::SSL::CertificateRevocationList.expects(:find).returns crl - @ca.crl.should equal(crl) - end - - it "should create, generate, and save a new CRL instance of no CRL can be found" do - crl = mock 'crl' - Puppet::SSL::CertificateRevocationList.expects(:find).returns nil - - Puppet::SSL::CertificateRevocationList.expects(:new).returns crl - - crl.expects(:generate).with(@ca.host.certificate.content, @ca.host.key.content) - crl.expects(:save) - - @ca.crl.should equal(crl) - end - end - - describe "when generating a self-signed CA certificate" do - before do - Puppet.settings.stubs(:use) - Puppet.settings.stubs(:value).returns "ca_testing" - - Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) - Puppet::SSL::CertificateAuthority.any_instance.stubs(:crl) - @ca = Puppet::SSL::CertificateAuthority.new - - @host = stub 'host', :key => mock("key"), :name => "hostname", :certificate => mock('certificate') - - Puppet::SSL::CertificateRequest.any_instance.stubs(:generate) - - @ca.stubs(:host).returns @host - end - - it "should create and store a password at :capass" do - Puppet.settings.expects(:value).with(:capass).returns "/path/to/pass" - - FileTest.expects(:exist?).with("/path/to/pass").returns false - - fh = mock 'filehandle' - Puppet.settings.expects(:write).with(:capass).yields fh - - fh.expects(:print).with { |s| s.length > 18 } - - @ca.stubs(:sign) - - @ca.generate_ca_certificate - end - - it "should generate a key if one does not exist" do - @ca.stubs :generate_password - @ca.stubs :sign - - @ca.host.expects(:key).returns nil - @ca.host.expects(:generate_key) - - @ca.generate_ca_certificate - end - - it "should create and sign a self-signed cert using the CA name" do - request = mock 'request' - Puppet::SSL::CertificateRequest.expects(:new).with(@ca.host.name).returns request - request.expects(:generate).with(@ca.host.key) - - @ca.expects(:sign).with(@host.name, :ca, request) - - @ca.stubs :generate_password - - @ca.generate_ca_certificate - end - - it "should generate its CRL" do - @ca.stubs :generate_password - @ca.stubs :sign - - @ca.host.expects(:key).returns nil - @ca.host.expects(:generate_key) - - @ca.expects(:crl) - - @ca.generate_ca_certificate - end - end - - describe "when signing" do - before do - Puppet.settings.stubs(:use) - - Puppet::SSL::CertificateAuthority.any_instance.stubs(:password?).returns true - - stub_ca_host - - Puppet::SSL::Host.expects(:new).with(Puppet::SSL::Host.ca_name).returns @host - - @ca = Puppet::SSL::CertificateAuthority.new - - @name = "myhost" - @real_cert = stub 'realcert', :sign => nil - @cert = stub 'certificate', :content => @real_cert - - Puppet::SSL::Certificate.stubs(:new).returns @cert - - @cert.stubs(:content=) - @cert.stubs(:save) - - # Stub out the factory - @factory = stub 'factory', :result => "my real cert" - Puppet::SSL::CertificateFactory.stubs(:new).returns @factory - - @request = stub 'request', :content => "myrequest", :name => @name - - # And the inventory - @inventory = stub 'inventory', :add => nil - @ca.stubs(:inventory).returns @inventory - - Puppet::SSL::CertificateRequest.stubs(:destroy) - end - - describe "and calculating the next certificate serial number" do - before do - @path = "/path/to/serial" - Puppet.settings.stubs(:value).with(:serial).returns @path - - @filehandle = stub 'filehandle', :<< => @filehandle - Puppet.settings.stubs(:readwritelock).with(:serial).yields @filehandle - end - - it "should default to 0x1 for the first serial number" do - @ca.next_serial.should == 0x1 - end - - it "should return the current content of the serial file" do - FileTest.stubs(:exist?).with(@path).returns true - File.expects(:read).with(@path).returns "0002" - - @ca.next_serial.should == 2 - end - - it "should write the next serial number to the serial file as hex" do - @filehandle.expects(:<<).with("0002") - - @ca.next_serial - end - - it "should lock the serial file while writing" do - Puppet.settings.expects(:readwritelock).with(:serial) - - @ca.next_serial - end - end - - describe "its own certificate" do - before do - @serial = 10 - @ca.stubs(:next_serial).returns @serial - end - - it "should not look up a certificate request for the host" do - Puppet::SSL::CertificateRequest.expects(:find).never - - @ca.sign(@name, :ca, @request) - end - - it "should use a certificate type of :ca" do - Puppet::SSL::CertificateFactory.expects(:new).with do |*args| - args[0] == :ca - end.returns @factory - @ca.sign(@name, :ca, @request) - end - - it "should pass the provided CSR as the CSR" do - Puppet::SSL::CertificateFactory.expects(:new).with do |*args| - args[1] == "myrequest" - end.returns @factory - @ca.sign(@name, :ca, @request) - end - - it "should use the provided CSR's content as the issuer" do - Puppet::SSL::CertificateFactory.expects(:new).with do |*args| - args[2] == "myrequest" - end.returns @factory - @ca.sign(@name, :ca, @request) - end - - it "should pass the next serial as the serial number" do - Puppet::SSL::CertificateFactory.expects(:new).with do |*args| - args[3] == @serial - end.returns @factory - @ca.sign(@name, :ca, @request) - end - - it "should save the resulting certificate" do - @cert.expects(:save) - - @ca.sign(@name, :ca, @request) - end - end - - describe "another host's certificate" do - before do - @serial = 10 - @ca.stubs(:next_serial).returns @serial - - Puppet::SSL::CertificateRequest.stubs(:find).with(@name).returns @request - @cert.stubs :save - end - - it "should use a certificate type of :server" do - Puppet::SSL::CertificateFactory.expects(:new).with do |*args| - args[0] == :server - end.returns @factory - - @ca.sign(@name) - end - - it "should use look up a CSR for the host in the :ca_file terminus" do - Puppet::SSL::CertificateRequest.expects(:find).with(@name).returns @request - - @ca.sign(@name) - end - - it "should fail if no CSR can be found for the host" do - Puppet::SSL::CertificateRequest.expects(:find).with(@name).returns nil - - lambda { @ca.sign(@name) }.should raise_error(ArgumentError) - end - - it "should use the CA certificate as the issuer" do - Puppet::SSL::CertificateFactory.expects(:new).with do |*args| - args[2] == @cacert.content - end.returns @factory - @ca.sign(@name) - end - - it "should pass the next serial as the serial number" do - Puppet::SSL::CertificateFactory.expects(:new).with do |*args| - args[3] == @serial - end.returns @factory - @ca.sign(@name) - end - - it "should sign the resulting certificate using its real key and a digest" do - digest = mock 'digest' - OpenSSL::Digest::SHA1.expects(:new).returns digest - - key = stub 'key', :content => "real_key" - @ca.host.stubs(:key).returns key - - @cert.content.expects(:sign).with("real_key", digest) - @ca.sign(@name) - end - - it "should save the resulting certificate" do - @cert.expects(:save) - @ca.sign(@name) - end - - it "should remove the host's certificate request" do - Puppet::SSL::CertificateRequest.expects(:destroy).with(@name) - - @ca.sign(@name) - end - end - - it "should create a certificate instance with the content set to the newly signed x509 certificate" do - @serial = 10 - @ca.stubs(:next_serial).returns @serial - - Puppet::SSL::CertificateRequest.stubs(:find).with(@name).returns @request - @cert.stubs :save - Puppet::SSL::Certificate.expects(:new).with(@name).returns @cert - - @ca.sign(@name) - end - - it "should return the certificate instance" do - @ca.stubs(:next_serial).returns @serial - Puppet::SSL::CertificateRequest.stubs(:find).with(@name).returns @request - @cert.stubs :save - @ca.sign(@name).should equal(@cert) - end - - it "should add the certificate to its inventory" do - @ca.stubs(:next_serial).returns @serial - @inventory.expects(:add).with(@cert) - - Puppet::SSL::CertificateRequest.stubs(:find).with(@name).returns @request - @cert.stubs :save - @ca.sign(@name) - end - - it "should have a method for triggering autosigning of available CSRs" do - @ca.should respond_to(:autosign) - end - - describe "when autosigning certificates" do - it "should do nothing if autosign is disabled" do - Puppet.settings.expects(:value).with(:autosign).returns 'false' - - Puppet::SSL::CertificateRequest.expects(:search).never - @ca.autosign - end - - it "should do nothing if no autosign.conf exists" do - Puppet.settings.expects(:value).with(:autosign).returns '/auto/sign' - FileTest.expects(:exist?).with("/auto/sign").returns false - - Puppet::SSL::CertificateRequest.expects(:search).never - @ca.autosign - end - - describe "and autosign is enabled and the autosign.conf file exists" do - before do - Puppet.settings.stubs(:value).with(:autosign).returns '/auto/sign' - FileTest.stubs(:exist?).with("/auto/sign").returns true - File.stubs(:readlines).with("/auto/sign").returns ["one\n", "two\n"] - - Puppet::SSL::CertificateRequest.stubs(:search).returns [] - - @store = stub 'store', :allow => nil - Puppet::Network::AuthStore.stubs(:new).returns @store - end - - describe "when creating the AuthStore instance to verify autosigning" do - it "should create an AuthStore with each line in the configuration file allowed to be autosigned" do - Puppet::Network::AuthStore.expects(:new).returns @store - - @store.expects(:allow).with("one") - @store.expects(:allow).with("two") - - @ca.autosign - end - - it "should reparse the autosign configuration on each call" do - Puppet::Network::AuthStore.expects(:new).times(2).returns @store - - @ca.autosign - @ca.autosign - end - - it "should ignore comments" do - File.stubs(:readlines).with("/auto/sign").returns ["one\n", "#two\n"] - - @store.expects(:allow).with("one") - @ca.autosign - end - - it "should ignore blank lines" do - File.stubs(:readlines).with("/auto/sign").returns ["one\n", "\n"] - - @store.expects(:allow).with("one") - @ca.autosign - end - end - - it "should sign all CSRs whose hostname matches the autosign configuration" do - csr1 = mock 'csr1' - csr2 = mock 'csr2' - Puppet::SSL::CertificateRequest.stubs(:search).returns [csr1, csr2] - end - - it "should not sign CSRs whose hostname does not match the autosign configuration" do - csr1 = mock 'csr1' - csr2 = mock 'csr2' - Puppet::SSL::CertificateRequest.stubs(:search).returns [csr1, csr2] - end - end - end - end - - describe "when managing certificate clients" do - before do - Puppet.settings.stubs(:use) - - Puppet::SSL::CertificateAuthority.any_instance.stubs(:password?).returns true - - stub_ca_host - - Puppet::SSL::Host.expects(:new).returns @host - Puppet::SSL::CertificateAuthority.any_instance.stubs(:host).returns @host - - @cacert = mock 'certificate' - @cacert.stubs(:content).returns "cacertificate" - @ca = Puppet::SSL::CertificateAuthority.new - end - - it "should have a method for acting on the SSL files" do - @ca.should respond_to(:apply) - end - - describe "when applying a method to a set of hosts" do - it "should fail if no subjects have been specified" do - lambda { @ca.apply(:generate) }.should raise_error(ArgumentError) - end - - it "should create an Interface instance with the specified method and the options" do - Puppet::SSL::CertificateAuthority::Interface.expects(:new).with(:generate, :to => :host).returns(stub('applier', :apply => nil)) - @ca.apply(:generate, :to => :host) - end - - it "should apply the Interface with itself as the argument" do - applier = stub('applier') - applier.expects(:apply).with(@ca) - Puppet::SSL::CertificateAuthority::Interface.expects(:new).returns applier - @ca.apply(:generate, :to => :ca_testing) - end - end - - it "should be able to list waiting certificate requests" do - req1 = stub 'req1', :name => "one" - req2 = stub 'req2', :name => "two" - Puppet::SSL::CertificateRequest.expects(:search).with("*").returns [req1, req2] - - @ca.waiting?.should == %w{one two} - end - - it "should delegate removing hosts to the Host class" do - Puppet::SSL::Host.expects(:destroy).with("myhost") - - @ca.destroy("myhost") - end - - it "should be able to verify certificates" do - @ca.should respond_to(:verify) - end - - it "should list certificates as the sorted list of all existing signed certificates" do - cert1 = stub 'cert1', :name => "cert1" - cert2 = stub 'cert2', :name => "cert2" - Puppet::SSL::Certificate.expects(:search).with("*").returns [cert1, cert2] - @ca.list.should == %w{cert1 cert2} - end - - describe "and printing certificates" do - it "should return nil if the certificate cannot be found" do - Puppet::SSL::Certificate.expects(:find).with("myhost").returns nil - @ca.print("myhost").should be_nil - end - - it "should print certificates by calling :to_text on the host's certificate" do - cert1 = stub 'cert1', :name => "cert1", :to_text => "mytext" - Puppet::SSL::Certificate.expects(:find).with("myhost").returns cert1 - @ca.print("myhost").should == "mytext" - end - end - - describe "and fingerprinting certificates" do - before :each do - @cert = stub 'cert', :name => "cert", :fingerprint => "DIGEST" - Puppet::SSL::Certificate.stubs(:find).with("myhost").returns @cert - Puppet::SSL::CertificateRequest.stubs(:find).with("myhost") - end - - it "should raise an error if the certificate or CSR cannot be found" do - Puppet::SSL::Certificate.expects(:find).with("myhost").returns nil - Puppet::SSL::CertificateRequest.expects(:find).with("myhost").returns nil - lambda { @ca.fingerprint("myhost") }.should raise_error - end - - it "should try to find a CSR if no certificate can be found" do - Puppet::SSL::Certificate.expects(:find).with("myhost").returns nil - Puppet::SSL::CertificateRequest.expects(:find).with("myhost").returns @cert - @cert.expects(:fingerprint) - @ca.fingerprint("myhost") - end - - it "should delegate to the certificate fingerprinting" do - @cert.expects(:fingerprint) - @ca.fingerprint("myhost") - end - - it "should propagate the digest algorithm to the certificate fingerprinting system" do - @cert.expects(:fingerprint).with(:digest) - @ca.fingerprint("myhost", :digest) - end - end - - describe "and verifying certificates" do - before do - @store = stub 'store', :verify => true, :add_file => nil, :purpose= => nil, :add_crl => true, :flags= => nil - - OpenSSL::X509::Store.stubs(:new).returns @store - - Puppet.settings.stubs(:value).returns "crtstuff" - - @cert = stub 'cert', :content => "mycert" - Puppet::SSL::Certificate.stubs(:find).returns @cert - - @crl = stub('crl', :content => "mycrl") - - @ca.stubs(:crl).returns @crl - end - - it "should fail if the host's certificate cannot be found" do - Puppet::SSL::Certificate.expects(:find).with("me").returns(nil) - - lambda { @ca.verify("me") }.should raise_error(ArgumentError) - end - - it "should create an SSL Store to verify" do - OpenSSL::X509::Store.expects(:new).returns @store - - @ca.verify("me") - end - - it "should add the CA Certificate to the store" do - Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" - @store.expects(:add_file).with "/ca/cert" - - @ca.verify("me") - end - - it "should add the CRL to the store if the crl is enabled" do - @store.expects(:add_crl).with "mycrl" - - @ca.verify("me") - end - - it "should set the store purpose to OpenSSL::X509::PURPOSE_SSL_CLIENT" do - Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" - @store.expects(:add_file).with "/ca/cert" - - @ca.verify("me") - end - - it "should set the store flags to check the crl" do - @store.expects(:flags=).with OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK - - @ca.verify("me") - end - - it "should use the store to verify the certificate" do - @cert.expects(:content).returns "mycert" - - @store.expects(:verify).with("mycert").returns true - - @ca.verify("me") - end - - it "should fail if the verification returns false" do - @cert.expects(:content).returns "mycert" - - @store.expects(:verify).with("mycert").returns false - - lambda { @ca.verify("me") }.should raise_error - end - end - - describe "and revoking certificates" do - before do - @crl = mock 'crl' - @ca.stubs(:crl).returns @crl - - @ca.stubs(:next_serial).returns 10 - - @real_cert = stub 'real_cert', :serial => 15 - @cert = stub 'cert', :content => @real_cert - Puppet::SSL::Certificate.stubs(:find).returns @cert - - end - - it "should fail if the certificate revocation list is disabled" do - @ca.stubs(:crl).returns false - - lambda { @ca.revoke('ca_testing') }.should raise_error(ArgumentError) - - end - - it "should delegate the revocation to its CRL" do - @ca.crl.expects(:revoke) - - @ca.revoke('host') - end - - it "should get the serial number from the local certificate if it exists" do - @ca.crl.expects(:revoke).with { |serial, key| serial == 15 } - - Puppet::SSL::Certificate.expects(:find).with("host").returns @cert - - @ca.revoke('host') - end - - it "should get the serial number from inventory if no local certificate exists" do - real_cert = stub 'real_cert', :serial => 15 - cert = stub 'cert', :content => real_cert - Puppet::SSL::Certificate.expects(:find).with("host").returns nil - - @ca.inventory.expects(:serial).with("host").returns 16 - - @ca.crl.expects(:revoke).with { |serial, key| serial == 16 } - @ca.revoke('host') - end - end - - it "should be able to generate a complete new SSL host" do - @ca.should respond_to(:generate) - end - - describe "and generating certificates" do - before do - @host = stub 'host', :generate_certificate_request => nil - Puppet::SSL::Host.stubs(:new).returns @host - Puppet::SSL::Certificate.stubs(:find).returns nil - - @ca.stubs(:sign) - end - - it "should fail if a certificate already exists for the host" do - Puppet::SSL::Certificate.expects(:find).with("him").returns "something" - - lambda { @ca.generate("him") }.should raise_error(ArgumentError) - end - - it "should create a new Host instance with the correct name" do - Puppet::SSL::Host.expects(:new).with("him").returns @host - - @ca.generate("him") - end - - it "should use the Host to generate the certificate request" do - @host.expects :generate_certificate_request - - @ca.generate("him") - end - - it "should sign the generated request" do - @ca.expects(:sign).with("him") - - @ca.generate("him") - end - end - end -end diff --git a/spec/unit/ssl/certificate_factory_spec.rb b/spec/unit/ssl/certificate_factory_spec.rb new file mode 100755 index 000000000..089ce3f43 --- /dev/null +++ b/spec/unit/ssl/certificate_factory_spec.rb @@ -0,0 +1,107 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/ssl/certificate_factory' + +describe Puppet::SSL::CertificateFactory do + before do + @cert_type = mock 'cert_type' + @name = mock 'name' + @csr = stub 'csr', :subject => @name + @issuer = mock 'issuer' + @serial = mock 'serial' + + @factory = Puppet::SSL::CertificateFactory.new(@cert_type, @csr, @issuer, @serial) + end + + describe "when initializing" do + it "should set its :cert_type to its first argument" do + @factory.cert_type.should equal(@cert_type) + end + + it "should set its :csr to its second argument" do + @factory.csr.should equal(@csr) + end + + it "should set its :issuer to its third argument" do + @factory.issuer.should equal(@issuer) + end + + it "should set its :serial to its fourth argument" do + @factory.serial.should equal(@serial) + end + + it "should set its name to the subject of the csr" do + @factory.name.should equal(@name) + end + end + + describe "when generating the certificate" do + before do + @cert = mock 'cert' + + @cert.stub_everything + + @factory.stubs :build_extensions + + @factory.stubs :set_ttl + + @issuer_name = mock 'issuer_name' + @issuer.stubs(:subject).returns @issuer_name + + @public_key = mock 'public_key' + @csr.stubs(:public_key).returns @public_key + + OpenSSL::X509::Certificate.stubs(:new).returns @cert + end + + it "should return a new X509 certificate" do + OpenSSL::X509::Certificate.expects(:new).returns @cert + @factory.result.should equal(@cert) + end + + it "should set the certificate's version to 2" do + @cert.expects(:version=).with 2 + @factory.result + end + + it "should set the certificate's subject to the CSR's subject" do + @cert.expects(:subject=).with @name + @factory.result + end + + it "should set the certificate's issuer to the Issuer's subject" do + @cert.expects(:issuer=).with @issuer_name + @factory.result + end + + it "should set the certificate's public key to the CSR's public key" do + @cert.expects(:public_key=).with @public_key + @factory.result + end + + it "should set the certificate's serial number to the provided serial number" do + @cert.expects(:serial=).with @serial + @factory.result + end + + it "should build extensions for the certificate" do + @factory.expects(:build_extensions) + @factory.result + end + + it "should set the ttl of the certificate" do + @factory.expects(:set_ttl) + @factory.result + end + end + + describe "when building extensions" do + it "should have tests" + end + + describe "when setting the ttl" do + it "should have tests" + end +end diff --git a/spec/unit/ssl/certificate_factory_spec_spec.rb b/spec/unit/ssl/certificate_factory_spec_spec.rb deleted file mode 100755 index 089ce3f43..000000000 --- a/spec/unit/ssl/certificate_factory_spec_spec.rb +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/ssl/certificate_factory' - -describe Puppet::SSL::CertificateFactory do - before do - @cert_type = mock 'cert_type' - @name = mock 'name' - @csr = stub 'csr', :subject => @name - @issuer = mock 'issuer' - @serial = mock 'serial' - - @factory = Puppet::SSL::CertificateFactory.new(@cert_type, @csr, @issuer, @serial) - end - - describe "when initializing" do - it "should set its :cert_type to its first argument" do - @factory.cert_type.should equal(@cert_type) - end - - it "should set its :csr to its second argument" do - @factory.csr.should equal(@csr) - end - - it "should set its :issuer to its third argument" do - @factory.issuer.should equal(@issuer) - end - - it "should set its :serial to its fourth argument" do - @factory.serial.should equal(@serial) - end - - it "should set its name to the subject of the csr" do - @factory.name.should equal(@name) - end - end - - describe "when generating the certificate" do - before do - @cert = mock 'cert' - - @cert.stub_everything - - @factory.stubs :build_extensions - - @factory.stubs :set_ttl - - @issuer_name = mock 'issuer_name' - @issuer.stubs(:subject).returns @issuer_name - - @public_key = mock 'public_key' - @csr.stubs(:public_key).returns @public_key - - OpenSSL::X509::Certificate.stubs(:new).returns @cert - end - - it "should return a new X509 certificate" do - OpenSSL::X509::Certificate.expects(:new).returns @cert - @factory.result.should equal(@cert) - end - - it "should set the certificate's version to 2" do - @cert.expects(:version=).with 2 - @factory.result - end - - it "should set the certificate's subject to the CSR's subject" do - @cert.expects(:subject=).with @name - @factory.result - end - - it "should set the certificate's issuer to the Issuer's subject" do - @cert.expects(:issuer=).with @issuer_name - @factory.result - end - - it "should set the certificate's public key to the CSR's public key" do - @cert.expects(:public_key=).with @public_key - @factory.result - end - - it "should set the certificate's serial number to the provided serial number" do - @cert.expects(:serial=).with @serial - @factory.result - end - - it "should build extensions for the certificate" do - @factory.expects(:build_extensions) - @factory.result - end - - it "should set the ttl of the certificate" do - @factory.expects(:set_ttl) - @factory.result - end - end - - describe "when building extensions" do - it "should have tests" - end - - describe "when setting the ttl" do - it "should have tests" - end -end diff --git a/spec/unit/ssl/certificate_request_spec.rb b/spec/unit/ssl/certificate_request_spec.rb new file mode 100755 index 000000000..253009b5d --- /dev/null +++ b/spec/unit/ssl/certificate_request_spec.rb @@ -0,0 +1,220 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/ssl/certificate_request' +require 'puppet/ssl/key' + +describe Puppet::SSL::CertificateRequest do + before do + @class = Puppet::SSL::CertificateRequest + end + + it "should be extended with the Indirector module" do + @class.singleton_class.should be_include(Puppet::Indirector) + end + + it "should indirect certificate_request" do + @class.indirection.name.should == :certificate_request + end + + it "should use any provided name as its name" do + @class.new("myname").name.should == "myname" + end + + it "should only support the text format" do + @class.supported_formats.should == [:s] + end + + describe "when converting from a string" do + it "should create a CSR instance with its name set to the CSR subject and its content set to the extracted CSR" do + csr = stub 'csr', :subject => "/CN=Foo.madstop.com" + OpenSSL::X509::Request.expects(:new).with("my csr").returns(csr) + + mycsr = stub 'sslcsr' + mycsr.expects(:content=).with(csr) + + @class.expects(:new).with("foo.madstop.com").returns mycsr + + @class.from_s("my csr") + end + end + + describe "when managing instances" do + before do + @request = @class.new("myname") + end + + it "should have a name attribute" do + @request.name.should == "myname" + end + + it "should downcase its name" do + @class.new("MyName").name.should == "myname" + end + + it "should have a content attribute" do + @request.should respond_to(:content) + end + + it "should be able to read requests from disk" do + path = "/my/path" + File.expects(:read).with(path).returns("my request") + request = mock 'request' + OpenSSL::X509::Request.expects(:new).with("my request").returns(request) + @request.read(path).should equal(request) + @request.content.should equal(request) + end + + it "should return an empty string when converted to a string with no request" do + @request.to_s.should == "" + end + + it "should convert the request to pem format when converted to a string" do + request = mock 'request', :to_pem => "pem" + @request.content = request + @request.to_s.should == "pem" + end + + it "should have a :to_text method that it delegates to the actual key" do + real_request = mock 'request' + real_request.expects(:to_text).returns "requesttext" + @request.content = real_request + @request.to_text.should == "requesttext" + end + end + + describe "when generating" do + before do + @instance = @class.new("myname") + + key = Puppet::SSL::Key.new("myname") + @key = key.generate + + @request = OpenSSL::X509::Request.new + OpenSSL::X509::Request.expects(:new).returns(@request) + + @request.stubs(:verify).returns(true) + end + + it "should use the content of the provided key if the key is a Puppet::SSL::Key instance" do + key = Puppet::SSL::Key.new("test") + key.expects(:content).returns @key + + @request.expects(:sign).with{ |key, digest| key == @key } + @instance.generate(key) + end + + it "should log that it is creating a new certificate request" do + Puppet.expects(:info).twice + @instance.generate(@key) + end + + it "should set the subject to [CN, name]" do + subject = mock 'subject' + OpenSSL::X509::Name.expects(:new).with([["CN", @instance.name]]).returns(subject) + @request.expects(:subject=).with(subject) + @instance.generate(@key) + end + + it "should set the CN to the CSR name when the CSR is not for a CA" do + subject = mock 'subject' + OpenSSL::X509::Name.expects(:new).with { |subject| subject[0][1] == @instance.name }.returns(subject) + @request.expects(:subject=).with(subject) + @instance.generate(@key) + end + + it "should set the CN to the :ca_name setting when the CSR is for a CA" do + subject = mock 'subject' + Puppet.settings.expects(:value).with(:ca_name).returns "mycertname" + OpenSSL::X509::Name.expects(:new).with { |subject| subject[0][1] == "mycertname" }.returns(subject) + @request.expects(:subject=).with(subject) + Puppet::SSL::CertificateRequest.new(Puppet::SSL::CA_NAME).generate(@key) + end + + it "should set the version to 0" do + @request.expects(:version=).with(0) + @instance.generate(@key) + end + + it "should set the public key to the provided key's public key" do + # Yay, the private key extracts a new key each time. + pubkey = @key.public_key + @key.stubs(:public_key).returns pubkey + @request.expects(:public_key=).with(@key.public_key) + @instance.generate(@key) + end + + it "should sign the csr with the provided key and a digest" do + digest = mock 'digest' + OpenSSL::Digest::MD5.expects(:new).returns(digest) + @request.expects(:sign).with(@key, digest) + @instance.generate(@key) + end + + it "should verify the generated request using the public key" do + # Stupid keys don't have a competent == method. + @request.expects(:verify).with { |public_key| public_key.to_s == @key.public_key.to_s }.returns true + @instance.generate(@key) + end + + it "should fail if verification fails" do + @request.expects(:verify).returns false + + lambda { @instance.generate(@key) }.should raise_error(Puppet::Error) + end + + it "should fingerprint the request" do + @instance.expects(:fingerprint) + @instance.generate(@key) + end + + it "should display the fingerprint" do + Puppet.stubs(:info) + @instance.stubs(:fingerprint).returns("FINGERPRINT") + Puppet.expects(:info).with { |s| s =~ /FINGERPRINT/ } + @instance.generate(@key) + end + + it "should return the generated request" do + @instance.generate(@key).should equal(@request) + end + + it "should set its content to the generated request" do + @instance.generate(@key) + @instance.content.should equal(@request) + end + end + + describe "when a CSR is saved" do + it "should allow arguments" do + csr = Puppet::SSL::CertificateRequest.new("me") + csr.class.indirection.stubs(:save) + + lambda { csr.save :ipaddress => "foo" }.should_not raise_error + end + + describe "and a CA is available" do + it "should save the CSR and trigger autosigning" do + ca = mock 'ca', :autosign + Puppet::SSL::CertificateAuthority.expects(:instance).returns ca + + csr = Puppet::SSL::CertificateRequest.new("me") + Puppet::SSL::CertificateRequest.indirection.expects(:save).with(nil, csr) + + csr.save + end + end + + describe "and a CA is not available" do + it "should save the CSR" do + Puppet::SSL::CertificateAuthority.expects(:instance).returns nil + + csr = Puppet::SSL::CertificateRequest.new("me") + Puppet::SSL::CertificateRequest.indirection.expects(:save).with(nil, csr) + + csr.save + end + end + end +end diff --git a/spec/unit/ssl/certificate_request_spec_spec.rb b/spec/unit/ssl/certificate_request_spec_spec.rb deleted file mode 100755 index 253009b5d..000000000 --- a/spec/unit/ssl/certificate_request_spec_spec.rb +++ /dev/null @@ -1,220 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/ssl/certificate_request' -require 'puppet/ssl/key' - -describe Puppet::SSL::CertificateRequest do - before do - @class = Puppet::SSL::CertificateRequest - end - - it "should be extended with the Indirector module" do - @class.singleton_class.should be_include(Puppet::Indirector) - end - - it "should indirect certificate_request" do - @class.indirection.name.should == :certificate_request - end - - it "should use any provided name as its name" do - @class.new("myname").name.should == "myname" - end - - it "should only support the text format" do - @class.supported_formats.should == [:s] - end - - describe "when converting from a string" do - it "should create a CSR instance with its name set to the CSR subject and its content set to the extracted CSR" do - csr = stub 'csr', :subject => "/CN=Foo.madstop.com" - OpenSSL::X509::Request.expects(:new).with("my csr").returns(csr) - - mycsr = stub 'sslcsr' - mycsr.expects(:content=).with(csr) - - @class.expects(:new).with("foo.madstop.com").returns mycsr - - @class.from_s("my csr") - end - end - - describe "when managing instances" do - before do - @request = @class.new("myname") - end - - it "should have a name attribute" do - @request.name.should == "myname" - end - - it "should downcase its name" do - @class.new("MyName").name.should == "myname" - end - - it "should have a content attribute" do - @request.should respond_to(:content) - end - - it "should be able to read requests from disk" do - path = "/my/path" - File.expects(:read).with(path).returns("my request") - request = mock 'request' - OpenSSL::X509::Request.expects(:new).with("my request").returns(request) - @request.read(path).should equal(request) - @request.content.should equal(request) - end - - it "should return an empty string when converted to a string with no request" do - @request.to_s.should == "" - end - - it "should convert the request to pem format when converted to a string" do - request = mock 'request', :to_pem => "pem" - @request.content = request - @request.to_s.should == "pem" - end - - it "should have a :to_text method that it delegates to the actual key" do - real_request = mock 'request' - real_request.expects(:to_text).returns "requesttext" - @request.content = real_request - @request.to_text.should == "requesttext" - end - end - - describe "when generating" do - before do - @instance = @class.new("myname") - - key = Puppet::SSL::Key.new("myname") - @key = key.generate - - @request = OpenSSL::X509::Request.new - OpenSSL::X509::Request.expects(:new).returns(@request) - - @request.stubs(:verify).returns(true) - end - - it "should use the content of the provided key if the key is a Puppet::SSL::Key instance" do - key = Puppet::SSL::Key.new("test") - key.expects(:content).returns @key - - @request.expects(:sign).with{ |key, digest| key == @key } - @instance.generate(key) - end - - it "should log that it is creating a new certificate request" do - Puppet.expects(:info).twice - @instance.generate(@key) - end - - it "should set the subject to [CN, name]" do - subject = mock 'subject' - OpenSSL::X509::Name.expects(:new).with([["CN", @instance.name]]).returns(subject) - @request.expects(:subject=).with(subject) - @instance.generate(@key) - end - - it "should set the CN to the CSR name when the CSR is not for a CA" do - subject = mock 'subject' - OpenSSL::X509::Name.expects(:new).with { |subject| subject[0][1] == @instance.name }.returns(subject) - @request.expects(:subject=).with(subject) - @instance.generate(@key) - end - - it "should set the CN to the :ca_name setting when the CSR is for a CA" do - subject = mock 'subject' - Puppet.settings.expects(:value).with(:ca_name).returns "mycertname" - OpenSSL::X509::Name.expects(:new).with { |subject| subject[0][1] == "mycertname" }.returns(subject) - @request.expects(:subject=).with(subject) - Puppet::SSL::CertificateRequest.new(Puppet::SSL::CA_NAME).generate(@key) - end - - it "should set the version to 0" do - @request.expects(:version=).with(0) - @instance.generate(@key) - end - - it "should set the public key to the provided key's public key" do - # Yay, the private key extracts a new key each time. - pubkey = @key.public_key - @key.stubs(:public_key).returns pubkey - @request.expects(:public_key=).with(@key.public_key) - @instance.generate(@key) - end - - it "should sign the csr with the provided key and a digest" do - digest = mock 'digest' - OpenSSL::Digest::MD5.expects(:new).returns(digest) - @request.expects(:sign).with(@key, digest) - @instance.generate(@key) - end - - it "should verify the generated request using the public key" do - # Stupid keys don't have a competent == method. - @request.expects(:verify).with { |public_key| public_key.to_s == @key.public_key.to_s }.returns true - @instance.generate(@key) - end - - it "should fail if verification fails" do - @request.expects(:verify).returns false - - lambda { @instance.generate(@key) }.should raise_error(Puppet::Error) - end - - it "should fingerprint the request" do - @instance.expects(:fingerprint) - @instance.generate(@key) - end - - it "should display the fingerprint" do - Puppet.stubs(:info) - @instance.stubs(:fingerprint).returns("FINGERPRINT") - Puppet.expects(:info).with { |s| s =~ /FINGERPRINT/ } - @instance.generate(@key) - end - - it "should return the generated request" do - @instance.generate(@key).should equal(@request) - end - - it "should set its content to the generated request" do - @instance.generate(@key) - @instance.content.should equal(@request) - end - end - - describe "when a CSR is saved" do - it "should allow arguments" do - csr = Puppet::SSL::CertificateRequest.new("me") - csr.class.indirection.stubs(:save) - - lambda { csr.save :ipaddress => "foo" }.should_not raise_error - end - - describe "and a CA is available" do - it "should save the CSR and trigger autosigning" do - ca = mock 'ca', :autosign - Puppet::SSL::CertificateAuthority.expects(:instance).returns ca - - csr = Puppet::SSL::CertificateRequest.new("me") - Puppet::SSL::CertificateRequest.indirection.expects(:save).with(nil, csr) - - csr.save - end - end - - describe "and a CA is not available" do - it "should save the CSR" do - Puppet::SSL::CertificateAuthority.expects(:instance).returns nil - - csr = Puppet::SSL::CertificateRequest.new("me") - Puppet::SSL::CertificateRequest.indirection.expects(:save).with(nil, csr) - - csr.save - end - end - end -end diff --git a/spec/unit/ssl/certificate_revocation_list_spec.rb b/spec/unit/ssl/certificate_revocation_list_spec.rb new file mode 100755 index 000000000..3d15db78b --- /dev/null +++ b/spec/unit/ssl/certificate_revocation_list_spec.rb @@ -0,0 +1,168 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/ssl/certificate_revocation_list' + +describe Puppet::SSL::CertificateRevocationList do + before do + @cert = stub 'cert', :subject => "mysubject" + @key = stub 'key', :private? => true + + @class = Puppet::SSL::CertificateRevocationList + end + + it "should only support the text format" do + @class.supported_formats.should == [:s] + end + + describe "when converting from a string" do + it "should create a CRL instance with its name set to 'foo' and its content set to the extracted CRL" do + crl = stub 'crl' + OpenSSL::X509::CRL.expects(:new).returns(crl) + + mycrl = stub 'sslcrl' + mycrl.expects(:content=).with(crl) + + @class.expects(:new).with("foo").returns mycrl + + @class.from_s("my crl").should == mycrl + end + end + + describe "when an instance" do + before do + @class.any_instance.stubs(:read_or_generate) + + @crl = @class.new("whatever") + end + + it "should always use 'crl' for its name" do + @crl.name.should == "crl" + end + + it "should have a content attribute" do + @crl.should respond_to(:content) + end + end + + describe "when generating the crl" do + before do + @real_crl = mock 'crl' + @real_crl.stub_everything + + OpenSSL::X509::CRL.stubs(:new).returns(@real_crl) + + @class.any_instance.stubs(:read_or_generate) + + @crl = @class.new("crl") + end + + it "should set its issuer to the subject of the passed certificate" do + @real_crl.expects(:issuer=).with(@cert.subject) + + @crl.generate(@cert, @key) + end + + it "should set its version to 1" do + @real_crl.expects(:version=).with(1) + + @crl.generate(@cert, @key) + end + + it "should create an instance of OpenSSL::X509::CRL" do + OpenSSL::X509::CRL.expects(:new).returns(@real_crl) + + @crl.generate(@cert, @key) + end + + # The next three tests aren't good, but at least they + # specify the behaviour. + it "should add an extension for the CRL number" do + @real_crl.expects(:extensions=) + @crl.generate(@cert, @key) + end + + it "should set the last update time" do + @real_crl.expects(:last_update=) + @crl.generate(@cert, @key) + end + + it "should set the next update time" do + @real_crl.expects(:next_update=) + @crl.generate(@cert, @key) + end + + it "should sign the CRL" do + @real_crl.expects(:sign).with { |key, digest| key == @key } + @crl.generate(@cert, @key) + end + + it "should set the content to the generated crl" do + @crl.generate(@cert, @key) + @crl.content.should equal(@real_crl) + end + + it "should return the generated crl" do + @crl.generate(@cert, @key).should equal(@real_crl) + end + end + + # This test suite isn't exactly complete, because the + # SSL stuff is very complicated. It just hits the high points. + describe "when revoking a certificate" do + before do + @class.wrapped_class.any_instance.stubs(:issuer=) + @class.wrapped_class.any_instance.stubs(:sign) + + @crl = @class.new("crl") + @crl.generate(@cert, @key) + @crl.content.stubs(:sign) + + @crl.stubs :save + + @key = mock 'key' + end + + it "should require a serial number and the CA's private key" do + lambda { @crl.revoke }.should raise_error(ArgumentError) + end + + it "should default to OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE as the revocation reason" do + # This makes it a bit more of an integration test than we'd normally like, but that's life + # with openssl. + reason = OpenSSL::ASN1::Enumerated(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE) + OpenSSL::ASN1.expects(:Enumerated).with(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE).returns reason + + @crl.revoke(1, @key) + end + + it "should mark the CRL as updated" do + time = Time.now + Time.stubs(:now).returns time + + @crl.content.expects(:last_update=).with(time) + + @crl.revoke(1, @key) + end + + it "should mark the CRL valid for five years" do + time = Time.now + Time.stubs(:now).returns time + + @crl.content.expects(:next_update=).with(time + (5 * 365*24*60*60)) + + @crl.revoke(1, @key) + end + + it "should sign the CRL with the CA's private key and a digest instance" do + @crl.content.expects(:sign).with { |key, digest| key == @key and digest.is_a?(OpenSSL::Digest::SHA1) } + @crl.revoke(1, @key) + end + + it "should save the CRL" do + @crl.expects :save + @crl.revoke(1, @key) + end + end +end diff --git a/spec/unit/ssl/certificate_revocation_list_spec_spec.rb b/spec/unit/ssl/certificate_revocation_list_spec_spec.rb deleted file mode 100755 index 3d15db78b..000000000 --- a/spec/unit/ssl/certificate_revocation_list_spec_spec.rb +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/ssl/certificate_revocation_list' - -describe Puppet::SSL::CertificateRevocationList do - before do - @cert = stub 'cert', :subject => "mysubject" - @key = stub 'key', :private? => true - - @class = Puppet::SSL::CertificateRevocationList - end - - it "should only support the text format" do - @class.supported_formats.should == [:s] - end - - describe "when converting from a string" do - it "should create a CRL instance with its name set to 'foo' and its content set to the extracted CRL" do - crl = stub 'crl' - OpenSSL::X509::CRL.expects(:new).returns(crl) - - mycrl = stub 'sslcrl' - mycrl.expects(:content=).with(crl) - - @class.expects(:new).with("foo").returns mycrl - - @class.from_s("my crl").should == mycrl - end - end - - describe "when an instance" do - before do - @class.any_instance.stubs(:read_or_generate) - - @crl = @class.new("whatever") - end - - it "should always use 'crl' for its name" do - @crl.name.should == "crl" - end - - it "should have a content attribute" do - @crl.should respond_to(:content) - end - end - - describe "when generating the crl" do - before do - @real_crl = mock 'crl' - @real_crl.stub_everything - - OpenSSL::X509::CRL.stubs(:new).returns(@real_crl) - - @class.any_instance.stubs(:read_or_generate) - - @crl = @class.new("crl") - end - - it "should set its issuer to the subject of the passed certificate" do - @real_crl.expects(:issuer=).with(@cert.subject) - - @crl.generate(@cert, @key) - end - - it "should set its version to 1" do - @real_crl.expects(:version=).with(1) - - @crl.generate(@cert, @key) - end - - it "should create an instance of OpenSSL::X509::CRL" do - OpenSSL::X509::CRL.expects(:new).returns(@real_crl) - - @crl.generate(@cert, @key) - end - - # The next three tests aren't good, but at least they - # specify the behaviour. - it "should add an extension for the CRL number" do - @real_crl.expects(:extensions=) - @crl.generate(@cert, @key) - end - - it "should set the last update time" do - @real_crl.expects(:last_update=) - @crl.generate(@cert, @key) - end - - it "should set the next update time" do - @real_crl.expects(:next_update=) - @crl.generate(@cert, @key) - end - - it "should sign the CRL" do - @real_crl.expects(:sign).with { |key, digest| key == @key } - @crl.generate(@cert, @key) - end - - it "should set the content to the generated crl" do - @crl.generate(@cert, @key) - @crl.content.should equal(@real_crl) - end - - it "should return the generated crl" do - @crl.generate(@cert, @key).should equal(@real_crl) - end - end - - # This test suite isn't exactly complete, because the - # SSL stuff is very complicated. It just hits the high points. - describe "when revoking a certificate" do - before do - @class.wrapped_class.any_instance.stubs(:issuer=) - @class.wrapped_class.any_instance.stubs(:sign) - - @crl = @class.new("crl") - @crl.generate(@cert, @key) - @crl.content.stubs(:sign) - - @crl.stubs :save - - @key = mock 'key' - end - - it "should require a serial number and the CA's private key" do - lambda { @crl.revoke }.should raise_error(ArgumentError) - end - - it "should default to OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE as the revocation reason" do - # This makes it a bit more of an integration test than we'd normally like, but that's life - # with openssl. - reason = OpenSSL::ASN1::Enumerated(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE) - OpenSSL::ASN1.expects(:Enumerated).with(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE).returns reason - - @crl.revoke(1, @key) - end - - it "should mark the CRL as updated" do - time = Time.now - Time.stubs(:now).returns time - - @crl.content.expects(:last_update=).with(time) - - @crl.revoke(1, @key) - end - - it "should mark the CRL valid for five years" do - time = Time.now - Time.stubs(:now).returns time - - @crl.content.expects(:next_update=).with(time + (5 * 365*24*60*60)) - - @crl.revoke(1, @key) - end - - it "should sign the CRL with the CA's private key and a digest instance" do - @crl.content.expects(:sign).with { |key, digest| key == @key and digest.is_a?(OpenSSL::Digest::SHA1) } - @crl.revoke(1, @key) - end - - it "should save the CRL" do - @crl.expects :save - @crl.revoke(1, @key) - end - end -end diff --git a/spec/unit/ssl/certificate_spec.rb b/spec/unit/ssl/certificate_spec.rb new file mode 100755 index 000000000..6bd7e77f5 --- /dev/null +++ b/spec/unit/ssl/certificate_spec.rb @@ -0,0 +1,124 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/ssl/certificate' + +describe Puppet::SSL::Certificate do + before do + @class = Puppet::SSL::Certificate + end + + after do + @class.instance_variable_set("@ca_location", nil) + end + + it "should be extended with the Indirector module" do + @class.singleton_class.should be_include(Puppet::Indirector) + end + + it "should indirect certificate" do + @class.indirection.name.should == :certificate + end + + it "should only support the text format" do + @class.supported_formats.should == [:s] + end + + describe "when converting from a string" do + it "should create a certificate instance with its name set to the certificate subject and its content set to the extracted certificate" do + cert = stub 'certificate', :subject => "/CN=Foo.madstop.com" + OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(cert) + + mycert = stub 'sslcert' + mycert.expects(:content=).with(cert) + + @class.expects(:new).with("foo.madstop.com").returns mycert + + @class.from_s("my certificate") + end + + it "should create multiple certificate instances when asked" do + cert1 = stub 'cert1' + @class.expects(:from_s).with("cert1").returns cert1 + cert2 = stub 'cert2' + @class.expects(:from_s).with("cert2").returns cert2 + + @class.from_multiple_s("cert1\n---\ncert2").should == [cert1, cert2] + end + end + + describe "when converting to a string" do + before do + @certificate = @class.new("myname") + end + + it "should return an empty string when it has no certificate" do + @certificate.to_s.should == "" + end + + it "should convert the certificate to pem format" do + certificate = mock 'certificate', :to_pem => "pem" + @certificate.content = certificate + @certificate.to_s.should == "pem" + end + + it "should be able to convert multiple instances to a string" do + cert2 = @class.new("foo") + @certificate.expects(:to_s).returns "cert1" + cert2.expects(:to_s).returns "cert2" + + @class.to_multiple_s([@certificate, cert2]).should == "cert1\n---\ncert2" + + end + end + + describe "when managing instances" do + before do + @certificate = @class.new("myname") + end + + it "should have a name attribute" do + @certificate.name.should == "myname" + end + + it "should convert its name to a string and downcase it" do + @class.new(:MyName).name.should == "myname" + end + + it "should have a content attribute" do + @certificate.should respond_to(:content) + end + + it "should return a nil expiration if there is no actual certificate" do + @certificate.stubs(:content).returns nil + + @certificate.expiration.should be_nil + end + + it "should use the expiration of the certificate as its expiration date" do + cert = stub 'cert' + @certificate.stubs(:content).returns cert + + cert.expects(:not_after).returns "sometime" + + @certificate.expiration.should == "sometime" + end + + it "should be able to read certificates from disk" do + path = "/my/path" + File.expects(:read).with(path).returns("my certificate") + certificate = mock 'certificate' + OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(certificate) + @certificate.read(path).should equal(certificate) + @certificate.content.should equal(certificate) + end + + it "should have a :to_text method that it delegates to the actual key" do + real_certificate = mock 'certificate' + real_certificate.expects(:to_text).returns "certificatetext" + @certificate.content = real_certificate + @certificate.to_text.should == "certificatetext" + end + end +end diff --git a/spec/unit/ssl/certificate_spec_spec.rb b/spec/unit/ssl/certificate_spec_spec.rb deleted file mode 100755 index 6bd7e77f5..000000000 --- a/spec/unit/ssl/certificate_spec_spec.rb +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/ssl/certificate' - -describe Puppet::SSL::Certificate do - before do - @class = Puppet::SSL::Certificate - end - - after do - @class.instance_variable_set("@ca_location", nil) - end - - it "should be extended with the Indirector module" do - @class.singleton_class.should be_include(Puppet::Indirector) - end - - it "should indirect certificate" do - @class.indirection.name.should == :certificate - end - - it "should only support the text format" do - @class.supported_formats.should == [:s] - end - - describe "when converting from a string" do - it "should create a certificate instance with its name set to the certificate subject and its content set to the extracted certificate" do - cert = stub 'certificate', :subject => "/CN=Foo.madstop.com" - OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(cert) - - mycert = stub 'sslcert' - mycert.expects(:content=).with(cert) - - @class.expects(:new).with("foo.madstop.com").returns mycert - - @class.from_s("my certificate") - end - - it "should create multiple certificate instances when asked" do - cert1 = stub 'cert1' - @class.expects(:from_s).with("cert1").returns cert1 - cert2 = stub 'cert2' - @class.expects(:from_s).with("cert2").returns cert2 - - @class.from_multiple_s("cert1\n---\ncert2").should == [cert1, cert2] - end - end - - describe "when converting to a string" do - before do - @certificate = @class.new("myname") - end - - it "should return an empty string when it has no certificate" do - @certificate.to_s.should == "" - end - - it "should convert the certificate to pem format" do - certificate = mock 'certificate', :to_pem => "pem" - @certificate.content = certificate - @certificate.to_s.should == "pem" - end - - it "should be able to convert multiple instances to a string" do - cert2 = @class.new("foo") - @certificate.expects(:to_s).returns "cert1" - cert2.expects(:to_s).returns "cert2" - - @class.to_multiple_s([@certificate, cert2]).should == "cert1\n---\ncert2" - - end - end - - describe "when managing instances" do - before do - @certificate = @class.new("myname") - end - - it "should have a name attribute" do - @certificate.name.should == "myname" - end - - it "should convert its name to a string and downcase it" do - @class.new(:MyName).name.should == "myname" - end - - it "should have a content attribute" do - @certificate.should respond_to(:content) - end - - it "should return a nil expiration if there is no actual certificate" do - @certificate.stubs(:content).returns nil - - @certificate.expiration.should be_nil - end - - it "should use the expiration of the certificate as its expiration date" do - cert = stub 'cert' - @certificate.stubs(:content).returns cert - - cert.expects(:not_after).returns "sometime" - - @certificate.expiration.should == "sometime" - end - - it "should be able to read certificates from disk" do - path = "/my/path" - File.expects(:read).with(path).returns("my certificate") - certificate = mock 'certificate' - OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(certificate) - @certificate.read(path).should equal(certificate) - @certificate.content.should equal(certificate) - end - - it "should have a :to_text method that it delegates to the actual key" do - real_certificate = mock 'certificate' - real_certificate.expects(:to_text).returns "certificatetext" - @certificate.content = real_certificate - @certificate.to_text.should == "certificatetext" - end - end -end diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb new file mode 100755 index 000000000..18cae2b62 --- /dev/null +++ b/spec/unit/ssl/host_spec.rb @@ -0,0 +1,707 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/ssl/host' + +describe Puppet::SSL::Host do + before do + @class = Puppet::SSL::Host + @host = @class.new("myname") + end + + after do + # Cleaned out any cached localhost instance. + Puppet::Util::Cacher.expire + end + + it "should use any provided name as its name" do + @host.name.should == "myname" + end + + it "should retrieve its public key from its private key" do + realkey = mock 'realkey' + key = stub 'key', :content => realkey + Puppet::SSL::Key.stubs(:find).returns(key) + pubkey = mock 'public_key' + realkey.expects(:public_key).returns pubkey + + @host.public_key.should equal(pubkey) + end + + it "should default to being a non-ca host" do + @host.ca?.should be_false + end + + it "should be a ca host if its name matches the CA_NAME" do + Puppet::SSL::Host.stubs(:ca_name).returns "yayca" + Puppet::SSL::Host.new("yayca").should be_ca + end + + it "should have a method for determining the CA location" do + Puppet::SSL::Host.should respond_to(:ca_location) + end + + it "should have a method for specifying the CA location" do + Puppet::SSL::Host.should respond_to(:ca_location=) + end + + it "should have a method for retrieving the default ssl host" do + Puppet::SSL::Host.should respond_to(:ca_location=) + end + + it "should have a method for producing an instance to manage the local host's keys" do + Puppet::SSL::Host.should respond_to(:localhost) + end + + it "should generate the certificate for the localhost instance if no certificate is available" do + host = stub 'host', :key => nil + Puppet::SSL::Host.expects(:new).returns host + + host.expects(:certificate).returns nil + host.expects(:generate) + + Puppet::SSL::Host.localhost.should equal(host) + end + + it "should always read the key for the localhost instance in from disk" do + host = stub 'host', :certificate => "eh" + Puppet::SSL::Host.expects(:new).returns host + + host.expects(:key) + + Puppet::SSL::Host.localhost + end + + it "should cache the localhost instance" do + host = stub 'host', :certificate => "eh", :key => 'foo' + Puppet::SSL::Host.expects(:new).once.returns host + + Puppet::SSL::Host.localhost.should == Puppet::SSL::Host.localhost + end + + it "should be able to expire the cached instance" do + one = stub 'host1', :certificate => "eh", :key => 'foo' + two = stub 'host2', :certificate => "eh", :key => 'foo' + Puppet::SSL::Host.expects(:new).times(2).returns(one).then.returns(two) + + Puppet::SSL::Host.localhost.should equal(one) + Puppet::Util::Cacher.expire + Puppet::SSL::Host.localhost.should equal(two) + end + + it "should be able to verify its certificate matches its key" do + Puppet::SSL::Host.new("foo").should respond_to(:certificate_matches_key?) + end + + it "should consider the certificate invalid if it cannot find a key" do + host = Puppet::SSL::Host.new("foo") + host.expects(:key).returns nil + + host.should_not be_certificate_matches_key + end + + it "should consider the certificate invalid if it cannot find a certificate" do + host = Puppet::SSL::Host.new("foo") + host.expects(:key).returns mock("key") + host.expects(:certificate).returns nil + + host.should_not be_certificate_matches_key + end + + it "should consider the certificate invalid if the SSL certificate's key verification fails" do + host = Puppet::SSL::Host.new("foo") + + key = mock 'key', :content => "private_key" + sslcert = mock 'sslcert' + certificate = mock 'cert', :content => sslcert + + host.stubs(:key).returns key + host.stubs(:certificate).returns certificate + + sslcert.expects(:check_private_key).with("private_key").returns false + + host.should_not be_certificate_matches_key + end + + it "should consider the certificate valid if the SSL certificate's key verification succeeds" do + host = Puppet::SSL::Host.new("foo") + + key = mock 'key', :content => "private_key" + sslcert = mock 'sslcert' + certificate = mock 'cert', :content => sslcert + + host.stubs(:key).returns key + host.stubs(:certificate).returns certificate + + sslcert.expects(:check_private_key).with("private_key").returns true + + host.should be_certificate_matches_key + end + + describe "when specifying the CA location" do + before do + [Puppet::SSL::Key, Puppet::SSL::Certificate, Puppet::SSL::CertificateRequest, Puppet::SSL::CertificateRevocationList].each do |klass| + klass.stubs(:terminus_class=) + klass.stubs(:cache_class=) + end + end + + it "should support the location ':local'" do + lambda { Puppet::SSL::Host.ca_location = :local }.should_not raise_error + end + + it "should support the location ':remote'" do + lambda { Puppet::SSL::Host.ca_location = :remote }.should_not raise_error + end + + it "should support the location ':none'" do + lambda { Puppet::SSL::Host.ca_location = :none }.should_not raise_error + end + + it "should support the location ':only'" do + lambda { Puppet::SSL::Host.ca_location = :only }.should_not raise_error + end + + it "should not support other modes" do + lambda { Puppet::SSL::Host.ca_location = :whatever }.should raise_error(ArgumentError) + end + + describe "as 'local'" do + it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do + Puppet::SSL::Certificate.expects(:cache_class=).with :file + Puppet::SSL::CertificateRequest.expects(:cache_class=).with :file + Puppet::SSL::CertificateRevocationList.expects(:cache_class=).with :file + + Puppet::SSL::Host.ca_location = :local + end + + it "should set the terminus class for Key as :file" do + Puppet::SSL::Key.expects(:terminus_class=).with :file + + Puppet::SSL::Host.ca_location = :local + end + + it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :ca" do + Puppet::SSL::Certificate.expects(:terminus_class=).with :ca + Puppet::SSL::CertificateRequest.expects(:terminus_class=).with :ca + Puppet::SSL::CertificateRevocationList.expects(:terminus_class=).with :ca + + Puppet::SSL::Host.ca_location = :local + end + end + + describe "as 'remote'" do + it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do + Puppet::SSL::Certificate.expects(:cache_class=).with :file + Puppet::SSL::CertificateRequest.expects(:cache_class=).with :file + Puppet::SSL::CertificateRevocationList.expects(:cache_class=).with :file + + Puppet::SSL::Host.ca_location = :remote + end + + it "should set the terminus class for Key as :file" do + Puppet::SSL::Key.expects(:terminus_class=).with :file + + Puppet::SSL::Host.ca_location = :remote + end + + it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :rest" do + Puppet::SSL::Certificate.expects(:terminus_class=).with :rest + Puppet::SSL::CertificateRequest.expects(:terminus_class=).with :rest + Puppet::SSL::CertificateRevocationList.expects(:terminus_class=).with :rest + + Puppet::SSL::Host.ca_location = :remote + end + end + + describe "as 'only'" do + it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :ca" do + Puppet::SSL::Key.expects(:terminus_class=).with :ca + Puppet::SSL::Certificate.expects(:terminus_class=).with :ca + Puppet::SSL::CertificateRequest.expects(:terminus_class=).with :ca + Puppet::SSL::CertificateRevocationList.expects(:terminus_class=).with :ca + + Puppet::SSL::Host.ca_location = :only + end + + it "should reset the cache class for Certificate, CertificateRevocationList, and CertificateRequest to nil" do + Puppet::SSL::Certificate.expects(:cache_class=).with nil + Puppet::SSL::CertificateRequest.expects(:cache_class=).with nil + Puppet::SSL::CertificateRevocationList.expects(:cache_class=).with nil + + Puppet::SSL::Host.ca_location = :only + end + end + + describe "as 'none'" do + it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :file" do + Puppet::SSL::Key.expects(:terminus_class=).with :file + Puppet::SSL::Certificate.expects(:terminus_class=).with :file + Puppet::SSL::CertificateRequest.expects(:terminus_class=).with :file + Puppet::SSL::CertificateRevocationList.expects(:terminus_class=).with :file + + Puppet::SSL::Host.ca_location = :none + end + end + end + + it "should have a class method for destroying all files related to a given host" do + Puppet::SSL::Host.should respond_to(:destroy) + end + + describe "when destroying a host's SSL files" do + before do + Puppet::SSL::Key.stubs(:destroy).returns false + Puppet::SSL::Certificate.stubs(:destroy).returns false + Puppet::SSL::CertificateRequest.stubs(:destroy).returns false + end + + it "should destroy its certificate, certificate request, and key" do + Puppet::SSL::Key.expects(:destroy).with("myhost") + Puppet::SSL::Certificate.expects(:destroy).with("myhost") + Puppet::SSL::CertificateRequest.expects(:destroy).with("myhost") + + Puppet::SSL::Host.destroy("myhost") + end + + it "should return true if any of the classes returned true" do + Puppet::SSL::Certificate.expects(:destroy).with("myhost").returns true + + Puppet::SSL::Host.destroy("myhost").should be_true + end + + it "should return false if none of the classes returned true" do + Puppet::SSL::Host.destroy("myhost").should be_false + end + end + + describe "when initializing" do + it "should default its name to the :certname setting" do + Puppet.settings.expects(:value).with(:certname).returns "myname" + + Puppet::SSL::Host.new.name.should == "myname" + end + + it "should downcase a passed in name" do + Puppet::SSL::Host.new("Host.Domain.Com").name.should == "host.domain.com" + end + + it "should downcase the certname if it's used" do + Puppet.settings.expects(:value).with(:certname).returns "Host.Domain.Com" + Puppet::SSL::Host.new().name.should == "host.domain.com" + end + + it "should indicate that it is a CA host if its name matches the ca_name constant" do + Puppet::SSL::Host.stubs(:ca_name).returns "myca" + Puppet::SSL::Host.new("myca").should be_ca + end + end + + describe "when managing its private key" do + before do + @realkey = "mykey" + @key = stub 'key', :content => @realkey + end + + it "should return nil if the key is not set and cannot be found" do + Puppet::SSL::Key.expects(:find).with("myname").returns(nil) + @host.key.should be_nil + end + + it "should find the key in the Key class and return the Puppet instance" do + Puppet::SSL::Key.expects(:find).with("myname").returns(@key) + @host.key.should equal(@key) + end + + it "should be able to generate and save a new key" do + Puppet::SSL::Key.expects(:new).with("myname").returns(@key) + + @key.expects(:generate) + @key.expects(:save) + + @host.generate_key.should be_true + @host.key.should equal(@key) + end + + it "should not retain keys that could not be saved" do + Puppet::SSL::Key.expects(:new).with("myname").returns(@key) + + @key.stubs(:generate) + @key.expects(:save).raises "eh" + + lambda { @host.generate_key }.should raise_error + @host.key.should be_nil + end + + it "should return any previously found key without requerying" do + Puppet::SSL::Key.expects(:find).with("myname").returns(@key).once + @host.key.should equal(@key) + @host.key.should equal(@key) + end + end + + describe "when managing its certificate request" do + before do + @realrequest = "real request" + @request = stub 'request', :content => @realrequest + end + + it "should return nil if the key is not set and cannot be found" do + Puppet::SSL::CertificateRequest.expects(:find).with("myname").returns(nil) + @host.certificate_request.should be_nil + end + + it "should find the request in the Key class and return it and return the Puppet SSL request" do + Puppet::SSL::CertificateRequest.expects(:find).with("myname").returns @request + + @host.certificate_request.should equal(@request) + end + + it "should generate a new key when generating the cert request if no key exists" do + Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request + + key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" + + @host.expects(:key).times(2).returns(nil).then.returns(key) + @host.expects(:generate_key).returns(key) + + @request.stubs(:generate) + @request.stubs(:save) + + @host.generate_certificate_request + end + + it "should be able to generate and save a new request using the private key" do + Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request + + key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" + @host.stubs(:key).returns(key) + @request.expects(:generate).with("mycontent") + @request.expects(:save) + + @host.generate_certificate_request.should be_true + @host.certificate_request.should equal(@request) + end + + it "should return any previously found request without requerying" do + Puppet::SSL::CertificateRequest.expects(:find).with("myname").returns(@request).once + + @host.certificate_request.should equal(@request) + @host.certificate_request.should equal(@request) + end + + it "should not keep its certificate request in memory if the request cannot be saved" do + Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request + + key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" + @host.stubs(:key).returns(key) + @request.stubs(:generate) + @request.expects(:save).raises "eh" + + lambda { @host.generate_certificate_request }.should raise_error + + @host.certificate_request.should be_nil + end + end + + describe "when managing its certificate" do + before do + @realcert = mock 'certificate' + @cert = stub 'cert', :content => @realcert + + @host.stubs(:key).returns mock("key") + @host.stubs(:certificate_matches_key?).returns true + end + + it "should find the CA certificate if it does not have a certificate" do + Puppet::SSL::Certificate.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") + Puppet::SSL::Certificate.stubs(:find).with("myname").returns @cert + + @host.certificate + end + + it "should not find the CA certificate if it is the CA host" do + @host.expects(:ca?).returns true + Puppet::SSL::Certificate.stubs(:find) + Puppet::SSL::Certificate.expects(:find).with(Puppet::SSL::CA_NAME).never + + @host.certificate + end + + it "should return nil if it cannot find a CA certificate" do + Puppet::SSL::Certificate.expects(:find).with(Puppet::SSL::CA_NAME).returns nil + Puppet::SSL::Certificate.expects(:find).with("myname").never + + @host.certificate.should be_nil + end + + it "should find the key if it does not have one" do + Puppet::SSL::Certificate.stubs(:find) + @host.expects(:key).returns mock("key") + + @host.certificate + end + + it "should generate the key if one cannot be found" do + Puppet::SSL::Certificate.stubs(:find) + + @host.expects(:key).returns nil + @host.expects(:generate_key) + + @host.certificate + end + + it "should find the certificate in the Certificate class and return the Puppet certificate instance" do + Puppet::SSL::Certificate.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") + Puppet::SSL::Certificate.expects(:find).with("myname").returns @cert + + @host.certificate.should equal(@cert) + end + + it "should fail if the found certificate does not match the private key" do + @host.expects(:certificate_matches_key?).returns false + + Puppet::SSL::Certificate.stubs(:find).returns @cert + + lambda { @host.certificate }.should raise_error(Puppet::Error) + end + + it "should return any previously found certificate" do + Puppet::SSL::Certificate.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") + Puppet::SSL::Certificate.expects(:find).with("myname").returns(@cert).once + + @host.certificate.should equal(@cert) + @host.certificate.should equal(@cert) + end + end + + it "should have a method for listing certificate hosts" do + Puppet::SSL::Host.should respond_to(:search) + end + + describe "when listing certificate hosts" do + it "should default to listing all clients with any file types" do + Puppet::SSL::Key.expects(:search).returns [] + Puppet::SSL::Certificate.expects(:search).returns [] + Puppet::SSL::CertificateRequest.expects(:search).returns [] + Puppet::SSL::Host.search + end + + it "should be able to list only clients with a key" do + Puppet::SSL::Key.expects(:search).returns [] + Puppet::SSL::Certificate.expects(:search).never + Puppet::SSL::CertificateRequest.expects(:search).never + Puppet::SSL::Host.search :for => Puppet::SSL::Key + end + + it "should be able to list only clients with a certificate" do + Puppet::SSL::Key.expects(:search).never + Puppet::SSL::Certificate.expects(:search).returns [] + Puppet::SSL::CertificateRequest.expects(:search).never + Puppet::SSL::Host.search :for => Puppet::SSL::Certificate + end + + it "should be able to list only clients with a certificate request" do + Puppet::SSL::Key.expects(:search).never + Puppet::SSL::Certificate.expects(:search).never + Puppet::SSL::CertificateRequest.expects(:search).returns [] + Puppet::SSL::Host.search :for => Puppet::SSL::CertificateRequest + end + + it "should return a Host instance created with the name of each found instance" do + key = stub 'key', :name => "key" + cert = stub 'cert', :name => "cert" + csr = stub 'csr', :name => "csr" + + Puppet::SSL::Key.expects(:search).returns [key] + Puppet::SSL::Certificate.expects(:search).returns [cert] + Puppet::SSL::CertificateRequest.expects(:search).returns [csr] + + returned = [] + %w{key cert csr}.each do |name| + result = mock(name) + returned << result + Puppet::SSL::Host.expects(:new).with(name).returns result + end + + result = Puppet::SSL::Host.search + returned.each do |r| + result.should be_include(r) + end + end + end + + it "should have a method for generating all necessary files" do + Puppet::SSL::Host.new("me").should respond_to(:generate) + end + + describe "when generating files" do + before do + @host = Puppet::SSL::Host.new("me") + @host.stubs(:generate_key) + @host.stubs(:generate_certificate_request) + end + + it "should generate a key if one is not present" do + @host.stubs(:key).returns nil + @host.expects(:generate_key) + + @host.generate + end + + it "should generate a certificate request if one is not present" do + @host.expects(:certificate_request).returns nil + @host.expects(:generate_certificate_request) + + @host.generate + end + + describe "and it can create a certificate authority" do + before do + @ca = mock 'ca' + Puppet::SSL::CertificateAuthority.stubs(:instance).returns @ca + end + + it "should use the CA to sign its certificate request if it does not have a certificate" do + @host.expects(:certificate).returns nil + + @ca.expects(:sign).with(@host.name) + + @host.generate + end + end + + describe "and it cannot create a certificate authority" do + before do + Puppet::SSL::CertificateAuthority.stubs(:instance).returns nil + end + + it "should seek its certificate" do + @host.expects(:certificate) + + @host.generate + end + end + end + + it "should have a method for creating an SSL store" do + Puppet::SSL::Host.new("me").should respond_to(:ssl_store) + end + + it "should always return the same store" do + host = Puppet::SSL::Host.new("foo") + store = mock 'store' + store.stub_everything + OpenSSL::X509::Store.expects(:new).returns store + host.ssl_store.should equal(host.ssl_store) + end + + describe "when creating an SSL store" do + before do + @host = Puppet::SSL::Host.new("me") + @store = mock 'store' + @store.stub_everything + OpenSSL::X509::Store.stubs(:new).returns @store + + Puppet.settings.stubs(:value).with(:localcacert).returns "ssl_host_testing" + + Puppet::SSL::CertificateRevocationList.stubs(:find).returns(nil) + end + + it "should accept a purpose" do + @store.expects(:purpose=).with "my special purpose" + @host.ssl_store("my special purpose") + end + + it "should default to OpenSSL::X509::PURPOSE_ANY as the purpose" do + @store.expects(:purpose=).with OpenSSL::X509::PURPOSE_ANY + @host.ssl_store + end + + it "should add the local CA cert file" do + Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert/file" + @store.expects(:add_file).with "/ca/cert/file" + @host.ssl_store + end + + describe "and a CRL is available" do + before do + @crl = stub 'crl', :content => "real_crl" + Puppet::SSL::CertificateRevocationList.stubs(:find).returns @crl + Puppet.settings.stubs(:value).with(:certificate_revocation).returns true + end + + it "should add the CRL" do + @store.expects(:add_crl).with "real_crl" + @host.ssl_store + end + + it "should set the flags to OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK" do + @store.expects(:flags=).with OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK + @host.ssl_store + end + end + end + + describe "when waiting for a cert" do + before do + @host = Puppet::SSL::Host.new("me") + end + + it "should generate its certificate request and attempt to read the certificate again if no certificate is found" do + @host.expects(:certificate).times(2).returns(nil).then.returns "foo" + @host.expects(:generate) + @host.wait_for_cert(1) + end + + it "should catch and log errors during CSR saving" do + @host.expects(:certificate).times(2).returns(nil).then.returns "foo" + @host.expects(:generate).raises(RuntimeError).then.returns nil + @host.stubs(:sleep) + @host.wait_for_cert(1) + end + + it "should sleep and retry after failures saving the CSR if waitforcert is enabled" do + @host.expects(:certificate).times(2).returns(nil).then.returns "foo" + @host.expects(:generate).raises(RuntimeError).then.returns nil + @host.expects(:sleep).with(1) + @host.wait_for_cert(1) + end + + it "should exit after failures saving the CSR of waitforcert is disabled" do + @host.expects(:certificate).returns(nil) + @host.expects(:generate).raises(RuntimeError) + @host.expects(:puts) + @host.expects(:exit).with(1).raises(SystemExit) + lambda { @host.wait_for_cert(0) }.should raise_error(SystemExit) + end + + it "should exit if the wait time is 0 and it can neither find nor retrieve a certificate" do + @host.stubs(:certificate).returns nil + @host.expects(:generate) + @host.expects(:puts) + @host.expects(:exit).with(1).raises(SystemExit) + lambda { @host.wait_for_cert(0) }.should raise_error(SystemExit) + end + + it "should sleep for the specified amount of time if no certificate is found after generating its certificate request" do + @host.expects(:certificate).times(3).returns(nil).then.returns(nil).then.returns "foo" + @host.expects(:generate) + + @host.expects(:sleep).with(1) + + @host.wait_for_cert(1) + end + + it "should catch and log exceptions during certificate retrieval" do + @host.expects(:certificate).times(3).returns(nil).then.raises(RuntimeError).then.returns("foo") + @host.stubs(:generate) + @host.stubs(:sleep) + + Puppet.expects(:err) + + @host.wait_for_cert(1) + end + end +end diff --git a/spec/unit/ssl/host_spec_spec.rb b/spec/unit/ssl/host_spec_spec.rb deleted file mode 100755 index 18cae2b62..000000000 --- a/spec/unit/ssl/host_spec_spec.rb +++ /dev/null @@ -1,707 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/ssl/host' - -describe Puppet::SSL::Host do - before do - @class = Puppet::SSL::Host - @host = @class.new("myname") - end - - after do - # Cleaned out any cached localhost instance. - Puppet::Util::Cacher.expire - end - - it "should use any provided name as its name" do - @host.name.should == "myname" - end - - it "should retrieve its public key from its private key" do - realkey = mock 'realkey' - key = stub 'key', :content => realkey - Puppet::SSL::Key.stubs(:find).returns(key) - pubkey = mock 'public_key' - realkey.expects(:public_key).returns pubkey - - @host.public_key.should equal(pubkey) - end - - it "should default to being a non-ca host" do - @host.ca?.should be_false - end - - it "should be a ca host if its name matches the CA_NAME" do - Puppet::SSL::Host.stubs(:ca_name).returns "yayca" - Puppet::SSL::Host.new("yayca").should be_ca - end - - it "should have a method for determining the CA location" do - Puppet::SSL::Host.should respond_to(:ca_location) - end - - it "should have a method for specifying the CA location" do - Puppet::SSL::Host.should respond_to(:ca_location=) - end - - it "should have a method for retrieving the default ssl host" do - Puppet::SSL::Host.should respond_to(:ca_location=) - end - - it "should have a method for producing an instance to manage the local host's keys" do - Puppet::SSL::Host.should respond_to(:localhost) - end - - it "should generate the certificate for the localhost instance if no certificate is available" do - host = stub 'host', :key => nil - Puppet::SSL::Host.expects(:new).returns host - - host.expects(:certificate).returns nil - host.expects(:generate) - - Puppet::SSL::Host.localhost.should equal(host) - end - - it "should always read the key for the localhost instance in from disk" do - host = stub 'host', :certificate => "eh" - Puppet::SSL::Host.expects(:new).returns host - - host.expects(:key) - - Puppet::SSL::Host.localhost - end - - it "should cache the localhost instance" do - host = stub 'host', :certificate => "eh", :key => 'foo' - Puppet::SSL::Host.expects(:new).once.returns host - - Puppet::SSL::Host.localhost.should == Puppet::SSL::Host.localhost - end - - it "should be able to expire the cached instance" do - one = stub 'host1', :certificate => "eh", :key => 'foo' - two = stub 'host2', :certificate => "eh", :key => 'foo' - Puppet::SSL::Host.expects(:new).times(2).returns(one).then.returns(two) - - Puppet::SSL::Host.localhost.should equal(one) - Puppet::Util::Cacher.expire - Puppet::SSL::Host.localhost.should equal(two) - end - - it "should be able to verify its certificate matches its key" do - Puppet::SSL::Host.new("foo").should respond_to(:certificate_matches_key?) - end - - it "should consider the certificate invalid if it cannot find a key" do - host = Puppet::SSL::Host.new("foo") - host.expects(:key).returns nil - - host.should_not be_certificate_matches_key - end - - it "should consider the certificate invalid if it cannot find a certificate" do - host = Puppet::SSL::Host.new("foo") - host.expects(:key).returns mock("key") - host.expects(:certificate).returns nil - - host.should_not be_certificate_matches_key - end - - it "should consider the certificate invalid if the SSL certificate's key verification fails" do - host = Puppet::SSL::Host.new("foo") - - key = mock 'key', :content => "private_key" - sslcert = mock 'sslcert' - certificate = mock 'cert', :content => sslcert - - host.stubs(:key).returns key - host.stubs(:certificate).returns certificate - - sslcert.expects(:check_private_key).with("private_key").returns false - - host.should_not be_certificate_matches_key - end - - it "should consider the certificate valid if the SSL certificate's key verification succeeds" do - host = Puppet::SSL::Host.new("foo") - - key = mock 'key', :content => "private_key" - sslcert = mock 'sslcert' - certificate = mock 'cert', :content => sslcert - - host.stubs(:key).returns key - host.stubs(:certificate).returns certificate - - sslcert.expects(:check_private_key).with("private_key").returns true - - host.should be_certificate_matches_key - end - - describe "when specifying the CA location" do - before do - [Puppet::SSL::Key, Puppet::SSL::Certificate, Puppet::SSL::CertificateRequest, Puppet::SSL::CertificateRevocationList].each do |klass| - klass.stubs(:terminus_class=) - klass.stubs(:cache_class=) - end - end - - it "should support the location ':local'" do - lambda { Puppet::SSL::Host.ca_location = :local }.should_not raise_error - end - - it "should support the location ':remote'" do - lambda { Puppet::SSL::Host.ca_location = :remote }.should_not raise_error - end - - it "should support the location ':none'" do - lambda { Puppet::SSL::Host.ca_location = :none }.should_not raise_error - end - - it "should support the location ':only'" do - lambda { Puppet::SSL::Host.ca_location = :only }.should_not raise_error - end - - it "should not support other modes" do - lambda { Puppet::SSL::Host.ca_location = :whatever }.should raise_error(ArgumentError) - end - - describe "as 'local'" do - it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do - Puppet::SSL::Certificate.expects(:cache_class=).with :file - Puppet::SSL::CertificateRequest.expects(:cache_class=).with :file - Puppet::SSL::CertificateRevocationList.expects(:cache_class=).with :file - - Puppet::SSL::Host.ca_location = :local - end - - it "should set the terminus class for Key as :file" do - Puppet::SSL::Key.expects(:terminus_class=).with :file - - Puppet::SSL::Host.ca_location = :local - end - - it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :ca" do - Puppet::SSL::Certificate.expects(:terminus_class=).with :ca - Puppet::SSL::CertificateRequest.expects(:terminus_class=).with :ca - Puppet::SSL::CertificateRevocationList.expects(:terminus_class=).with :ca - - Puppet::SSL::Host.ca_location = :local - end - end - - describe "as 'remote'" do - it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do - Puppet::SSL::Certificate.expects(:cache_class=).with :file - Puppet::SSL::CertificateRequest.expects(:cache_class=).with :file - Puppet::SSL::CertificateRevocationList.expects(:cache_class=).with :file - - Puppet::SSL::Host.ca_location = :remote - end - - it "should set the terminus class for Key as :file" do - Puppet::SSL::Key.expects(:terminus_class=).with :file - - Puppet::SSL::Host.ca_location = :remote - end - - it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :rest" do - Puppet::SSL::Certificate.expects(:terminus_class=).with :rest - Puppet::SSL::CertificateRequest.expects(:terminus_class=).with :rest - Puppet::SSL::CertificateRevocationList.expects(:terminus_class=).with :rest - - Puppet::SSL::Host.ca_location = :remote - end - end - - describe "as 'only'" do - it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :ca" do - Puppet::SSL::Key.expects(:terminus_class=).with :ca - Puppet::SSL::Certificate.expects(:terminus_class=).with :ca - Puppet::SSL::CertificateRequest.expects(:terminus_class=).with :ca - Puppet::SSL::CertificateRevocationList.expects(:terminus_class=).with :ca - - Puppet::SSL::Host.ca_location = :only - end - - it "should reset the cache class for Certificate, CertificateRevocationList, and CertificateRequest to nil" do - Puppet::SSL::Certificate.expects(:cache_class=).with nil - Puppet::SSL::CertificateRequest.expects(:cache_class=).with nil - Puppet::SSL::CertificateRevocationList.expects(:cache_class=).with nil - - Puppet::SSL::Host.ca_location = :only - end - end - - describe "as 'none'" do - it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :file" do - Puppet::SSL::Key.expects(:terminus_class=).with :file - Puppet::SSL::Certificate.expects(:terminus_class=).with :file - Puppet::SSL::CertificateRequest.expects(:terminus_class=).with :file - Puppet::SSL::CertificateRevocationList.expects(:terminus_class=).with :file - - Puppet::SSL::Host.ca_location = :none - end - end - end - - it "should have a class method for destroying all files related to a given host" do - Puppet::SSL::Host.should respond_to(:destroy) - end - - describe "when destroying a host's SSL files" do - before do - Puppet::SSL::Key.stubs(:destroy).returns false - Puppet::SSL::Certificate.stubs(:destroy).returns false - Puppet::SSL::CertificateRequest.stubs(:destroy).returns false - end - - it "should destroy its certificate, certificate request, and key" do - Puppet::SSL::Key.expects(:destroy).with("myhost") - Puppet::SSL::Certificate.expects(:destroy).with("myhost") - Puppet::SSL::CertificateRequest.expects(:destroy).with("myhost") - - Puppet::SSL::Host.destroy("myhost") - end - - it "should return true if any of the classes returned true" do - Puppet::SSL::Certificate.expects(:destroy).with("myhost").returns true - - Puppet::SSL::Host.destroy("myhost").should be_true - end - - it "should return false if none of the classes returned true" do - Puppet::SSL::Host.destroy("myhost").should be_false - end - end - - describe "when initializing" do - it "should default its name to the :certname setting" do - Puppet.settings.expects(:value).with(:certname).returns "myname" - - Puppet::SSL::Host.new.name.should == "myname" - end - - it "should downcase a passed in name" do - Puppet::SSL::Host.new("Host.Domain.Com").name.should == "host.domain.com" - end - - it "should downcase the certname if it's used" do - Puppet.settings.expects(:value).with(:certname).returns "Host.Domain.Com" - Puppet::SSL::Host.new().name.should == "host.domain.com" - end - - it "should indicate that it is a CA host if its name matches the ca_name constant" do - Puppet::SSL::Host.stubs(:ca_name).returns "myca" - Puppet::SSL::Host.new("myca").should be_ca - end - end - - describe "when managing its private key" do - before do - @realkey = "mykey" - @key = stub 'key', :content => @realkey - end - - it "should return nil if the key is not set and cannot be found" do - Puppet::SSL::Key.expects(:find).with("myname").returns(nil) - @host.key.should be_nil - end - - it "should find the key in the Key class and return the Puppet instance" do - Puppet::SSL::Key.expects(:find).with("myname").returns(@key) - @host.key.should equal(@key) - end - - it "should be able to generate and save a new key" do - Puppet::SSL::Key.expects(:new).with("myname").returns(@key) - - @key.expects(:generate) - @key.expects(:save) - - @host.generate_key.should be_true - @host.key.should equal(@key) - end - - it "should not retain keys that could not be saved" do - Puppet::SSL::Key.expects(:new).with("myname").returns(@key) - - @key.stubs(:generate) - @key.expects(:save).raises "eh" - - lambda { @host.generate_key }.should raise_error - @host.key.should be_nil - end - - it "should return any previously found key without requerying" do - Puppet::SSL::Key.expects(:find).with("myname").returns(@key).once - @host.key.should equal(@key) - @host.key.should equal(@key) - end - end - - describe "when managing its certificate request" do - before do - @realrequest = "real request" - @request = stub 'request', :content => @realrequest - end - - it "should return nil if the key is not set and cannot be found" do - Puppet::SSL::CertificateRequest.expects(:find).with("myname").returns(nil) - @host.certificate_request.should be_nil - end - - it "should find the request in the Key class and return it and return the Puppet SSL request" do - Puppet::SSL::CertificateRequest.expects(:find).with("myname").returns @request - - @host.certificate_request.should equal(@request) - end - - it "should generate a new key when generating the cert request if no key exists" do - Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request - - key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" - - @host.expects(:key).times(2).returns(nil).then.returns(key) - @host.expects(:generate_key).returns(key) - - @request.stubs(:generate) - @request.stubs(:save) - - @host.generate_certificate_request - end - - it "should be able to generate and save a new request using the private key" do - Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request - - key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" - @host.stubs(:key).returns(key) - @request.expects(:generate).with("mycontent") - @request.expects(:save) - - @host.generate_certificate_request.should be_true - @host.certificate_request.should equal(@request) - end - - it "should return any previously found request without requerying" do - Puppet::SSL::CertificateRequest.expects(:find).with("myname").returns(@request).once - - @host.certificate_request.should equal(@request) - @host.certificate_request.should equal(@request) - end - - it "should not keep its certificate request in memory if the request cannot be saved" do - Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request - - key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" - @host.stubs(:key).returns(key) - @request.stubs(:generate) - @request.expects(:save).raises "eh" - - lambda { @host.generate_certificate_request }.should raise_error - - @host.certificate_request.should be_nil - end - end - - describe "when managing its certificate" do - before do - @realcert = mock 'certificate' - @cert = stub 'cert', :content => @realcert - - @host.stubs(:key).returns mock("key") - @host.stubs(:certificate_matches_key?).returns true - end - - it "should find the CA certificate if it does not have a certificate" do - Puppet::SSL::Certificate.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") - Puppet::SSL::Certificate.stubs(:find).with("myname").returns @cert - - @host.certificate - end - - it "should not find the CA certificate if it is the CA host" do - @host.expects(:ca?).returns true - Puppet::SSL::Certificate.stubs(:find) - Puppet::SSL::Certificate.expects(:find).with(Puppet::SSL::CA_NAME).never - - @host.certificate - end - - it "should return nil if it cannot find a CA certificate" do - Puppet::SSL::Certificate.expects(:find).with(Puppet::SSL::CA_NAME).returns nil - Puppet::SSL::Certificate.expects(:find).with("myname").never - - @host.certificate.should be_nil - end - - it "should find the key if it does not have one" do - Puppet::SSL::Certificate.stubs(:find) - @host.expects(:key).returns mock("key") - - @host.certificate - end - - it "should generate the key if one cannot be found" do - Puppet::SSL::Certificate.stubs(:find) - - @host.expects(:key).returns nil - @host.expects(:generate_key) - - @host.certificate - end - - it "should find the certificate in the Certificate class and return the Puppet certificate instance" do - Puppet::SSL::Certificate.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") - Puppet::SSL::Certificate.expects(:find).with("myname").returns @cert - - @host.certificate.should equal(@cert) - end - - it "should fail if the found certificate does not match the private key" do - @host.expects(:certificate_matches_key?).returns false - - Puppet::SSL::Certificate.stubs(:find).returns @cert - - lambda { @host.certificate }.should raise_error(Puppet::Error) - end - - it "should return any previously found certificate" do - Puppet::SSL::Certificate.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") - Puppet::SSL::Certificate.expects(:find).with("myname").returns(@cert).once - - @host.certificate.should equal(@cert) - @host.certificate.should equal(@cert) - end - end - - it "should have a method for listing certificate hosts" do - Puppet::SSL::Host.should respond_to(:search) - end - - describe "when listing certificate hosts" do - it "should default to listing all clients with any file types" do - Puppet::SSL::Key.expects(:search).returns [] - Puppet::SSL::Certificate.expects(:search).returns [] - Puppet::SSL::CertificateRequest.expects(:search).returns [] - Puppet::SSL::Host.search - end - - it "should be able to list only clients with a key" do - Puppet::SSL::Key.expects(:search).returns [] - Puppet::SSL::Certificate.expects(:search).never - Puppet::SSL::CertificateRequest.expects(:search).never - Puppet::SSL::Host.search :for => Puppet::SSL::Key - end - - it "should be able to list only clients with a certificate" do - Puppet::SSL::Key.expects(:search).never - Puppet::SSL::Certificate.expects(:search).returns [] - Puppet::SSL::CertificateRequest.expects(:search).never - Puppet::SSL::Host.search :for => Puppet::SSL::Certificate - end - - it "should be able to list only clients with a certificate request" do - Puppet::SSL::Key.expects(:search).never - Puppet::SSL::Certificate.expects(:search).never - Puppet::SSL::CertificateRequest.expects(:search).returns [] - Puppet::SSL::Host.search :for => Puppet::SSL::CertificateRequest - end - - it "should return a Host instance created with the name of each found instance" do - key = stub 'key', :name => "key" - cert = stub 'cert', :name => "cert" - csr = stub 'csr', :name => "csr" - - Puppet::SSL::Key.expects(:search).returns [key] - Puppet::SSL::Certificate.expects(:search).returns [cert] - Puppet::SSL::CertificateRequest.expects(:search).returns [csr] - - returned = [] - %w{key cert csr}.each do |name| - result = mock(name) - returned << result - Puppet::SSL::Host.expects(:new).with(name).returns result - end - - result = Puppet::SSL::Host.search - returned.each do |r| - result.should be_include(r) - end - end - end - - it "should have a method for generating all necessary files" do - Puppet::SSL::Host.new("me").should respond_to(:generate) - end - - describe "when generating files" do - before do - @host = Puppet::SSL::Host.new("me") - @host.stubs(:generate_key) - @host.stubs(:generate_certificate_request) - end - - it "should generate a key if one is not present" do - @host.stubs(:key).returns nil - @host.expects(:generate_key) - - @host.generate - end - - it "should generate a certificate request if one is not present" do - @host.expects(:certificate_request).returns nil - @host.expects(:generate_certificate_request) - - @host.generate - end - - describe "and it can create a certificate authority" do - before do - @ca = mock 'ca' - Puppet::SSL::CertificateAuthority.stubs(:instance).returns @ca - end - - it "should use the CA to sign its certificate request if it does not have a certificate" do - @host.expects(:certificate).returns nil - - @ca.expects(:sign).with(@host.name) - - @host.generate - end - end - - describe "and it cannot create a certificate authority" do - before do - Puppet::SSL::CertificateAuthority.stubs(:instance).returns nil - end - - it "should seek its certificate" do - @host.expects(:certificate) - - @host.generate - end - end - end - - it "should have a method for creating an SSL store" do - Puppet::SSL::Host.new("me").should respond_to(:ssl_store) - end - - it "should always return the same store" do - host = Puppet::SSL::Host.new("foo") - store = mock 'store' - store.stub_everything - OpenSSL::X509::Store.expects(:new).returns store - host.ssl_store.should equal(host.ssl_store) - end - - describe "when creating an SSL store" do - before do - @host = Puppet::SSL::Host.new("me") - @store = mock 'store' - @store.stub_everything - OpenSSL::X509::Store.stubs(:new).returns @store - - Puppet.settings.stubs(:value).with(:localcacert).returns "ssl_host_testing" - - Puppet::SSL::CertificateRevocationList.stubs(:find).returns(nil) - end - - it "should accept a purpose" do - @store.expects(:purpose=).with "my special purpose" - @host.ssl_store("my special purpose") - end - - it "should default to OpenSSL::X509::PURPOSE_ANY as the purpose" do - @store.expects(:purpose=).with OpenSSL::X509::PURPOSE_ANY - @host.ssl_store - end - - it "should add the local CA cert file" do - Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert/file" - @store.expects(:add_file).with "/ca/cert/file" - @host.ssl_store - end - - describe "and a CRL is available" do - before do - @crl = stub 'crl', :content => "real_crl" - Puppet::SSL::CertificateRevocationList.stubs(:find).returns @crl - Puppet.settings.stubs(:value).with(:certificate_revocation).returns true - end - - it "should add the CRL" do - @store.expects(:add_crl).with "real_crl" - @host.ssl_store - end - - it "should set the flags to OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK" do - @store.expects(:flags=).with OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK - @host.ssl_store - end - end - end - - describe "when waiting for a cert" do - before do - @host = Puppet::SSL::Host.new("me") - end - - it "should generate its certificate request and attempt to read the certificate again if no certificate is found" do - @host.expects(:certificate).times(2).returns(nil).then.returns "foo" - @host.expects(:generate) - @host.wait_for_cert(1) - end - - it "should catch and log errors during CSR saving" do - @host.expects(:certificate).times(2).returns(nil).then.returns "foo" - @host.expects(:generate).raises(RuntimeError).then.returns nil - @host.stubs(:sleep) - @host.wait_for_cert(1) - end - - it "should sleep and retry after failures saving the CSR if waitforcert is enabled" do - @host.expects(:certificate).times(2).returns(nil).then.returns "foo" - @host.expects(:generate).raises(RuntimeError).then.returns nil - @host.expects(:sleep).with(1) - @host.wait_for_cert(1) - end - - it "should exit after failures saving the CSR of waitforcert is disabled" do - @host.expects(:certificate).returns(nil) - @host.expects(:generate).raises(RuntimeError) - @host.expects(:puts) - @host.expects(:exit).with(1).raises(SystemExit) - lambda { @host.wait_for_cert(0) }.should raise_error(SystemExit) - end - - it "should exit if the wait time is 0 and it can neither find nor retrieve a certificate" do - @host.stubs(:certificate).returns nil - @host.expects(:generate) - @host.expects(:puts) - @host.expects(:exit).with(1).raises(SystemExit) - lambda { @host.wait_for_cert(0) }.should raise_error(SystemExit) - end - - it "should sleep for the specified amount of time if no certificate is found after generating its certificate request" do - @host.expects(:certificate).times(3).returns(nil).then.returns(nil).then.returns "foo" - @host.expects(:generate) - - @host.expects(:sleep).with(1) - - @host.wait_for_cert(1) - end - - it "should catch and log exceptions during certificate retrieval" do - @host.expects(:certificate).times(3).returns(nil).then.raises(RuntimeError).then.returns("foo") - @host.stubs(:generate) - @host.stubs(:sleep) - - Puppet.expects(:err) - - @host.wait_for_cert(1) - end - end -end diff --git a/spec/unit/ssl/inventory_spec.rb b/spec/unit/ssl/inventory_spec.rb new file mode 100755 index 000000000..bf1dbfb48 --- /dev/null +++ b/spec/unit/ssl/inventory_spec.rb @@ -0,0 +1,180 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/ssl/inventory' + +describe Puppet::SSL::Inventory do + before do + @class = Puppet::SSL::Inventory + end + + it "should use the :certinventory setting for the path to the inventory file" do + Puppet.settings.expects(:value).with(:cert_inventory).returns "/inven/tory" + + @class.any_instance.stubs(:rebuild) + + @class.new.path.should == "/inven/tory" + end + + describe "when initializing" do + it "should set its path to the inventory file" do + Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" + @class.new.path.should == "/inven/tory" + end + end + + describe "when managing an inventory" do + before do + Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" + + FileTest.stubs(:exist?).with("/inven/tory").returns true + + @inventory = @class.new + + @cert = mock 'cert' + end + + describe "and creating the inventory file" do + before do + Puppet.settings.stubs(:write) + FileTest.stubs(:exist?).with("/inven/tory").returns false + + Puppet::SSL::Certificate.stubs(:search).returns [] + end + + it "should log that it is building a new inventory file" do + Puppet.expects(:notice) + + @inventory.rebuild + end + + it "should use the Settings to write to the file" do + Puppet.settings.expects(:write).with(:cert_inventory) + + @inventory.rebuild + end + + it "should add a header to the file" do + fh = mock 'filehandle' + Puppet.settings.stubs(:write).yields fh + fh.expects(:print).with { |str| str =~ /^#/ } + + @inventory.rebuild + end + + it "should add formatted information on all existing certificates" do + cert1 = mock 'cert1' + cert2 = mock 'cert2' + + Puppet::SSL::Certificate.expects(:search).with("*").returns [cert1, cert2] + + @class.any_instance.expects(:add).with(cert1) + @class.any_instance.expects(:add).with(cert2) + + @inventory.rebuild + end + end + + describe "and adding a certificate" do + it "should build the inventory file if one does not exist" do + Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" + Puppet.settings.stubs(:write) + + FileTest.expects(:exist?).with("/inven/tory").returns false + + @inventory.expects(:rebuild) + + @inventory.add(@cert) + end + + it "should use the Settings to write to the file" do + Puppet.settings.expects(:write).with(:cert_inventory, "a") + + @inventory.add(@cert) + end + + it "should use the actual certificate if it was passed a Puppet certificate" do + cert = Puppet::SSL::Certificate.new("mycert") + cert.content = @cert + + fh = stub 'filehandle', :print => nil + Puppet.settings.stubs(:write).yields fh + + @inventory.expects(:format).with(@cert) + + @inventory.add(@cert) + end + + it "should add formatted certificate information to the end of the file" do + fh = mock 'filehandle' + + Puppet.settings.stubs(:write).yields fh + + @inventory.expects(:format).with(@cert).returns "myformat" + + fh.expects(:print).with("myformat") + + @inventory.add(@cert) + end + end + + describe "and formatting a certificate" do + before do + @cert = stub 'cert', :not_before => Time.now, :not_after => Time.now, :subject => "mycert", :serial => 15 + end + + it "should print the serial number as a 4 digit hex number in the first field" do + @inventory.format(@cert).split[0].should == "0x000f" # 15 in hex + end + + it "should print the not_before date in '%Y-%m-%dT%H:%M:%S%Z' format in the second field" do + @cert.not_before.expects(:strftime).with('%Y-%m-%dT%H:%M:%S%Z').returns "before_time" + + @inventory.format(@cert).split[1].should == "before_time" + end + + it "should print the not_after date in '%Y-%m-%dT%H:%M:%S%Z' format in the third field" do + @cert.not_after.expects(:strftime).with('%Y-%m-%dT%H:%M:%S%Z').returns "after_time" + + @inventory.format(@cert).split[2].should == "after_time" + end + + it "should print the subject in the fourth field" do + @inventory.format(@cert).split[3].should == "mycert" + end + + it "should add a carriage return" do + @inventory.format(@cert).should =~ /\n$/ + end + + it "should produce a line consisting of the serial number, start date, expiration date, and subject" do + # Just make sure our serial and subject bracket the lines. + @inventory.format(@cert).should =~ /^0x.+mycert$/ + end + end + + it "should be able to find a given host's serial number" do + @inventory.should respond_to(:serial) + end + + describe "and finding a serial number" do + it "should return nil if the inventory file is missing" do + FileTest.expects(:exist?).with("/inven/tory").returns false + @inventory.serial(:whatever).should be_nil + end + + it "should return the serial number from the line matching the provided name" do + File.expects(:readlines).with("/inven/tory").returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n"] + + @inventory.serial("me").should == 15 + end + + it "should return the number as an integer" do + File.expects(:readlines).with("/inven/tory").returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n"] + + @inventory.serial("me").should == 15 + end + end + end +end diff --git a/spec/unit/ssl/inventory_spec_spec.rb b/spec/unit/ssl/inventory_spec_spec.rb deleted file mode 100755 index bf1dbfb48..000000000 --- a/spec/unit/ssl/inventory_spec_spec.rb +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/ssl/inventory' - -describe Puppet::SSL::Inventory do - before do - @class = Puppet::SSL::Inventory - end - - it "should use the :certinventory setting for the path to the inventory file" do - Puppet.settings.expects(:value).with(:cert_inventory).returns "/inven/tory" - - @class.any_instance.stubs(:rebuild) - - @class.new.path.should == "/inven/tory" - end - - describe "when initializing" do - it "should set its path to the inventory file" do - Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" - @class.new.path.should == "/inven/tory" - end - end - - describe "when managing an inventory" do - before do - Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" - - FileTest.stubs(:exist?).with("/inven/tory").returns true - - @inventory = @class.new - - @cert = mock 'cert' - end - - describe "and creating the inventory file" do - before do - Puppet.settings.stubs(:write) - FileTest.stubs(:exist?).with("/inven/tory").returns false - - Puppet::SSL::Certificate.stubs(:search).returns [] - end - - it "should log that it is building a new inventory file" do - Puppet.expects(:notice) - - @inventory.rebuild - end - - it "should use the Settings to write to the file" do - Puppet.settings.expects(:write).with(:cert_inventory) - - @inventory.rebuild - end - - it "should add a header to the file" do - fh = mock 'filehandle' - Puppet.settings.stubs(:write).yields fh - fh.expects(:print).with { |str| str =~ /^#/ } - - @inventory.rebuild - end - - it "should add formatted information on all existing certificates" do - cert1 = mock 'cert1' - cert2 = mock 'cert2' - - Puppet::SSL::Certificate.expects(:search).with("*").returns [cert1, cert2] - - @class.any_instance.expects(:add).with(cert1) - @class.any_instance.expects(:add).with(cert2) - - @inventory.rebuild - end - end - - describe "and adding a certificate" do - it "should build the inventory file if one does not exist" do - Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" - Puppet.settings.stubs(:write) - - FileTest.expects(:exist?).with("/inven/tory").returns false - - @inventory.expects(:rebuild) - - @inventory.add(@cert) - end - - it "should use the Settings to write to the file" do - Puppet.settings.expects(:write).with(:cert_inventory, "a") - - @inventory.add(@cert) - end - - it "should use the actual certificate if it was passed a Puppet certificate" do - cert = Puppet::SSL::Certificate.new("mycert") - cert.content = @cert - - fh = stub 'filehandle', :print => nil - Puppet.settings.stubs(:write).yields fh - - @inventory.expects(:format).with(@cert) - - @inventory.add(@cert) - end - - it "should add formatted certificate information to the end of the file" do - fh = mock 'filehandle' - - Puppet.settings.stubs(:write).yields fh - - @inventory.expects(:format).with(@cert).returns "myformat" - - fh.expects(:print).with("myformat") - - @inventory.add(@cert) - end - end - - describe "and formatting a certificate" do - before do - @cert = stub 'cert', :not_before => Time.now, :not_after => Time.now, :subject => "mycert", :serial => 15 - end - - it "should print the serial number as a 4 digit hex number in the first field" do - @inventory.format(@cert).split[0].should == "0x000f" # 15 in hex - end - - it "should print the not_before date in '%Y-%m-%dT%H:%M:%S%Z' format in the second field" do - @cert.not_before.expects(:strftime).with('%Y-%m-%dT%H:%M:%S%Z').returns "before_time" - - @inventory.format(@cert).split[1].should == "before_time" - end - - it "should print the not_after date in '%Y-%m-%dT%H:%M:%S%Z' format in the third field" do - @cert.not_after.expects(:strftime).with('%Y-%m-%dT%H:%M:%S%Z').returns "after_time" - - @inventory.format(@cert).split[2].should == "after_time" - end - - it "should print the subject in the fourth field" do - @inventory.format(@cert).split[3].should == "mycert" - end - - it "should add a carriage return" do - @inventory.format(@cert).should =~ /\n$/ - end - - it "should produce a line consisting of the serial number, start date, expiration date, and subject" do - # Just make sure our serial and subject bracket the lines. - @inventory.format(@cert).should =~ /^0x.+mycert$/ - end - end - - it "should be able to find a given host's serial number" do - @inventory.should respond_to(:serial) - end - - describe "and finding a serial number" do - it "should return nil if the inventory file is missing" do - FileTest.expects(:exist?).with("/inven/tory").returns false - @inventory.serial(:whatever).should be_nil - end - - it "should return the serial number from the line matching the provided name" do - File.expects(:readlines).with("/inven/tory").returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n"] - - @inventory.serial("me").should == 15 - end - - it "should return the number as an integer" do - File.expects(:readlines).with("/inven/tory").returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n"] - - @inventory.serial("me").should == 15 - end - end - end -end diff --git a/spec/unit/ssl/key_spec.rb b/spec/unit/ssl/key_spec.rb new file mode 100755 index 000000000..cfeaf7906 --- /dev/null +++ b/spec/unit/ssl/key_spec.rb @@ -0,0 +1,198 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/ssl/key' + +describe Puppet::SSL::Key do + before do + @class = Puppet::SSL::Key + end + + it "should be extended with the Indirector module" do + @class.singleton_class.should be_include(Puppet::Indirector) + end + + it "should indirect key" do + @class.indirection.name.should == :key + end + + it "should default to the :file terminus" do + @class.indirection.terminus_class.should == :file + end + + it "should only support the text format" do + @class.supported_formats.should == [:s] + end + + it "should have a method for determining whether it's a CA key" do + @class.new("test").should respond_to(:ca?) + end + + it "should consider itself a ca key if its name matches the CA_NAME" do + @class.new(Puppet::SSL::Host.ca_name).should be_ca + end + + describe "when initializing" do + it "should set its password file to the :capass if it's a CA key" do + Puppet.settings.stubs(:value).returns "whatever" + Puppet.settings.stubs(:value).with(:capass).returns "/ca/pass" + + key = Puppet::SSL::Key.new(Puppet::SSL::Host.ca_name) + key.password_file.should == "/ca/pass" + end + + it "should downcase its name" do + @class.new("MyName").name.should == "myname" + end + + it "should set its password file to the default password file if it is not the CA key" do + Puppet.settings.stubs(:value).returns "whatever" + Puppet.settings.stubs(:value).with(:passfile).returns "/normal/pass" + + key = Puppet::SSL::Key.new("notca") + key.password_file.should == "/normal/pass" + end + end + + describe "when managing instances" do + before do + @key = @class.new("myname") + end + + it "should have a name attribute" do + @key.name.should == "myname" + end + + it "should have a content attribute" do + @key.should respond_to(:content) + end + + it "should be able to read keys from disk" do + path = "/my/path" + File.expects(:read).with(path).returns("my key") + key = mock 'key' + OpenSSL::PKey::RSA.expects(:new).returns(key) + @key.read(path).should equal(key) + @key.content.should equal(key) + end + + it "should not try to use the provided password file if the file does not exist" do + FileTest.stubs(:exist?).returns false + @key.password_file = "/path/to/password" + + path = "/my/path" + + File.stubs(:read).with(path).returns("my key") + OpenSSL::PKey::RSA.expects(:new).with("my key", nil).returns(mock('key')) + File.expects(:read).with("/path/to/password").never + + @key.read(path) + end + + it "should read the key with the password retrieved from the password file if one is provided" do + FileTest.stubs(:exist?).returns true + @key.password_file = "/path/to/password" + + path = "/my/path" + File.expects(:read).with(path).returns("my key") + File.expects(:read).with("/path/to/password").returns("my password") + + key = mock 'key' + OpenSSL::PKey::RSA.expects(:new).with("my key", "my password").returns(key) + @key.read(path).should equal(key) + @key.content.should equal(key) + end + + it "should return an empty string when converted to a string with no key" do + @key.to_s.should == "" + end + + it "should convert the key to pem format when converted to a string" do + key = mock 'key', :to_pem => "pem" + @key.content = key + @key.to_s.should == "pem" + end + + it "should have a :to_text method that it delegates to the actual key" do + real_key = mock 'key' + real_key.expects(:to_text).returns "keytext" + @key.content = real_key + @key.to_text.should == "keytext" + end + end + + describe "when generating the private key" do + before do + @instance = @class.new("test") + + @key = mock 'key' + end + + it "should create an instance of OpenSSL::PKey::RSA" do + OpenSSL::PKey::RSA.expects(:new).returns(@key) + + @instance.generate + end + + it "should create the private key with the keylength specified in the settings" do + Puppet.settings.expects(:value).with(:keylength).returns("50") + OpenSSL::PKey::RSA.expects(:new).with(50).returns(@key) + + @instance.generate + end + + it "should set the content to the generated key" do + OpenSSL::PKey::RSA.stubs(:new).returns(@key) + @instance.generate + @instance.content.should equal(@key) + end + + it "should return the generated key" do + OpenSSL::PKey::RSA.stubs(:new).returns(@key) + @instance.generate.should equal(@key) + end + + it "should return the key in pem format" do + @instance.generate + @instance.content.expects(:to_pem).returns "my normal key" + @instance.to_s.should == "my normal key" + end + + describe "with a password file set" do + it "should return a nil password if the password file does not exist" do + FileTest.expects(:exist?).with("/path/to/pass").returns false + File.expects(:read).with("/path/to/pass").never + + @instance.password_file = "/path/to/pass" + + @instance.password.should be_nil + end + + it "should return the contents of the password file as its password" do + FileTest.expects(:exist?).with("/path/to/pass").returns true + File.expects(:read).with("/path/to/pass").returns "my password" + + @instance.password_file = "/path/to/pass" + + @instance.password.should == "my password" + end + + it "should export the private key to text using the password" do + Puppet.settings.stubs(:value).with(:keylength).returns("50") + + @instance.password_file = "/path/to/pass" + @instance.stubs(:password).returns "my password" + + OpenSSL::PKey::RSA.expects(:new).returns(@key) + @instance.generate + + cipher = mock 'cipher' + OpenSSL::Cipher::DES.expects(:new).with(:EDE3, :CBC).returns cipher + @key.expects(:export).with(cipher, "my password").returns "my encrypted key" + + @instance.to_s.should == "my encrypted key" + end + end + end +end diff --git a/spec/unit/ssl/key_spec_spec.rb b/spec/unit/ssl/key_spec_spec.rb deleted file mode 100755 index cfeaf7906..000000000 --- a/spec/unit/ssl/key_spec_spec.rb +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/ssl/key' - -describe Puppet::SSL::Key do - before do - @class = Puppet::SSL::Key - end - - it "should be extended with the Indirector module" do - @class.singleton_class.should be_include(Puppet::Indirector) - end - - it "should indirect key" do - @class.indirection.name.should == :key - end - - it "should default to the :file terminus" do - @class.indirection.terminus_class.should == :file - end - - it "should only support the text format" do - @class.supported_formats.should == [:s] - end - - it "should have a method for determining whether it's a CA key" do - @class.new("test").should respond_to(:ca?) - end - - it "should consider itself a ca key if its name matches the CA_NAME" do - @class.new(Puppet::SSL::Host.ca_name).should be_ca - end - - describe "when initializing" do - it "should set its password file to the :capass if it's a CA key" do - Puppet.settings.stubs(:value).returns "whatever" - Puppet.settings.stubs(:value).with(:capass).returns "/ca/pass" - - key = Puppet::SSL::Key.new(Puppet::SSL::Host.ca_name) - key.password_file.should == "/ca/pass" - end - - it "should downcase its name" do - @class.new("MyName").name.should == "myname" - end - - it "should set its password file to the default password file if it is not the CA key" do - Puppet.settings.stubs(:value).returns "whatever" - Puppet.settings.stubs(:value).with(:passfile).returns "/normal/pass" - - key = Puppet::SSL::Key.new("notca") - key.password_file.should == "/normal/pass" - end - end - - describe "when managing instances" do - before do - @key = @class.new("myname") - end - - it "should have a name attribute" do - @key.name.should == "myname" - end - - it "should have a content attribute" do - @key.should respond_to(:content) - end - - it "should be able to read keys from disk" do - path = "/my/path" - File.expects(:read).with(path).returns("my key") - key = mock 'key' - OpenSSL::PKey::RSA.expects(:new).returns(key) - @key.read(path).should equal(key) - @key.content.should equal(key) - end - - it "should not try to use the provided password file if the file does not exist" do - FileTest.stubs(:exist?).returns false - @key.password_file = "/path/to/password" - - path = "/my/path" - - File.stubs(:read).with(path).returns("my key") - OpenSSL::PKey::RSA.expects(:new).with("my key", nil).returns(mock('key')) - File.expects(:read).with("/path/to/password").never - - @key.read(path) - end - - it "should read the key with the password retrieved from the password file if one is provided" do - FileTest.stubs(:exist?).returns true - @key.password_file = "/path/to/password" - - path = "/my/path" - File.expects(:read).with(path).returns("my key") - File.expects(:read).with("/path/to/password").returns("my password") - - key = mock 'key' - OpenSSL::PKey::RSA.expects(:new).with("my key", "my password").returns(key) - @key.read(path).should equal(key) - @key.content.should equal(key) - end - - it "should return an empty string when converted to a string with no key" do - @key.to_s.should == "" - end - - it "should convert the key to pem format when converted to a string" do - key = mock 'key', :to_pem => "pem" - @key.content = key - @key.to_s.should == "pem" - end - - it "should have a :to_text method that it delegates to the actual key" do - real_key = mock 'key' - real_key.expects(:to_text).returns "keytext" - @key.content = real_key - @key.to_text.should == "keytext" - end - end - - describe "when generating the private key" do - before do - @instance = @class.new("test") - - @key = mock 'key' - end - - it "should create an instance of OpenSSL::PKey::RSA" do - OpenSSL::PKey::RSA.expects(:new).returns(@key) - - @instance.generate - end - - it "should create the private key with the keylength specified in the settings" do - Puppet.settings.expects(:value).with(:keylength).returns("50") - OpenSSL::PKey::RSA.expects(:new).with(50).returns(@key) - - @instance.generate - end - - it "should set the content to the generated key" do - OpenSSL::PKey::RSA.stubs(:new).returns(@key) - @instance.generate - @instance.content.should equal(@key) - end - - it "should return the generated key" do - OpenSSL::PKey::RSA.stubs(:new).returns(@key) - @instance.generate.should equal(@key) - end - - it "should return the key in pem format" do - @instance.generate - @instance.content.expects(:to_pem).returns "my normal key" - @instance.to_s.should == "my normal key" - end - - describe "with a password file set" do - it "should return a nil password if the password file does not exist" do - FileTest.expects(:exist?).with("/path/to/pass").returns false - File.expects(:read).with("/path/to/pass").never - - @instance.password_file = "/path/to/pass" - - @instance.password.should be_nil - end - - it "should return the contents of the password file as its password" do - FileTest.expects(:exist?).with("/path/to/pass").returns true - File.expects(:read).with("/path/to/pass").returns "my password" - - @instance.password_file = "/path/to/pass" - - @instance.password.should == "my password" - end - - it "should export the private key to text using the password" do - Puppet.settings.stubs(:value).with(:keylength).returns("50") - - @instance.password_file = "/path/to/pass" - @instance.stubs(:password).returns "my password" - - OpenSSL::PKey::RSA.expects(:new).returns(@key) - @instance.generate - - cipher = mock 'cipher' - OpenSSL::Cipher::DES.expects(:new).with(:EDE3, :CBC).returns cipher - @key.expects(:export).with(cipher, "my password").returns "my encrypted key" - - @instance.to_s.should == "my encrypted key" - end - end - end -end diff --git a/spec/unit/status_spec.rb b/spec/unit/status_spec.rb new file mode 100644 index 000000000..334b9b50a --- /dev/null +++ b/spec/unit/status_spec.rb @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +describe Puppet::Status do + it "should implement find" do + Puppet::Status.find( :default ).should be_is_a(Puppet::Status) + Puppet::Status.find( :default ).status["is_alive"].should == true + end + + it "should default to is_alive is true" do + Puppet::Status.new.status["is_alive"].should == true + end + + it "should return a pson hash" do + Puppet::Status.new.status.to_pson.should == '{"is_alive":true}' + end + + it "should accept a hash from pson" do + status = Puppet::Status.new( { "is_alive" => false } ) + status.status.should == { "is_alive" => false } + end + + it "should have a name" do + Puppet::Status.new.name + end + + it "should allow a name to be set" do + Puppet::Status.new.name = "status" + end +end diff --git a/spec/unit/status_spec_spec.rb b/spec/unit/status_spec_spec.rb deleted file mode 100644 index 334b9b50a..000000000 --- a/spec/unit/status_spec_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -describe Puppet::Status do - it "should implement find" do - Puppet::Status.find( :default ).should be_is_a(Puppet::Status) - Puppet::Status.find( :default ).status["is_alive"].should == true - end - - it "should default to is_alive is true" do - Puppet::Status.new.status["is_alive"].should == true - end - - it "should return a pson hash" do - Puppet::Status.new.status.to_pson.should == '{"is_alive":true}' - end - - it "should accept a hash from pson" do - status = Puppet::Status.new( { "is_alive" => false } ) - status.status.should == { "is_alive" => false } - end - - it "should have a name" do - Puppet::Status.new.name - end - - it "should allow a name to be set" do - Puppet::Status.new.name = "status" - end -end diff --git a/spec/unit/transaction/change_spec.rb b/spec/unit/transaction/change_spec.rb new file mode 100755 index 000000000..9419bbab9 --- /dev/null +++ b/spec/unit/transaction/change_spec.rb @@ -0,0 +1,193 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/transaction/change' + +describe Puppet::Transaction::Change do + Change = Puppet::Transaction::Change + + describe "when initializing" do + before do + @property = stub 'property', :path => "/property/path", :should => "shouldval" + end + + it "should require the property and current value" do + lambda { Change.new() }.should raise_error + end + + it "should set its property to the provided property" do + Change.new(@property, "value").property.should == :property + end + + it "should set its 'is' value to the provided value" do + Change.new(@property, "value").is.should == "value" + end + + it "should retrieve the 'should' value from the property" do + # Yay rspec :) + Change.new(@property, "value").should.should == @property.should + end + end + + describe "when an instance" do + before do + @property = stub 'property', :path => "/property/path", :should => "shouldval" + @change = Change.new(@property, "value") + end + + it "should be noop if the property is noop" do + @property.expects(:noop).returns true + @change.noop?.should be_true + end + + it "should be auditing if set so" do + @change.auditing = true + @change.must be_auditing + end + + it "should set its resource to the proxy if it has one" do + @change.proxy = :myresource + @change.resource.should == :myresource + end + + it "should set its resource to the property's resource if no proxy is set" do + @property.expects(:resource).returns :myresource + @change.resource.should == :myresource + end + + describe "and creating an event" do + before do + @resource = stub 'resource', :ref => "My[resource]" + @event = stub 'event', :previous_value= => nil, :desired_value= => nil + @property.stubs(:event).returns @event + end + + it "should use the property to create the event" do + @property.expects(:event).returns @event + @change.event.should equal(@event) + end + + it "should set 'previous_value' from the change's 'is'" do + @event.expects(:previous_value=).with(@change.is) + @change.event + end + + it "should set 'desired_value' from the change's 'should'" do + @event.expects(:desired_value=).with(@change.should) + @change.event + end + end + + describe "and executing" do + before do + @event = Puppet::Transaction::Event.new(:myevent) + @event.stubs(:send_log) + @change.stubs(:noop?).returns false + @property.stubs(:event).returns @event + + @property.stub_everything + @property.stubs(:resource).returns "myresource" + @property.stubs(:name).returns :myprop + end + + describe "in noop mode" do + before { @change.stubs(:noop?).returns true } + + it "should log that it is in noop" do + @property.expects(:is_to_s) + @property.expects(:should_to_s) + + @event.expects(:message=).with { |msg| msg.include?("should be") } + + @change.apply + end + + it "should produce a :noop event and return" do + @property.stub_everything + + @event.expects(:status=).with("noop") + + @change.apply.should == @event + end + end + + describe "in audit mode" do + before { @change.auditing = true } + + it "should log that it is in audit mode" do + @property.expects(:is_to_s) + @property.expects(:should_to_s) + + @event.expects(:message=).with { |msg| msg.include?("audit") } + + @change.apply + end + + it "should produce a :audit event and return" do + @property.stub_everything + + @event.expects(:status=).with("audit") + + @change.apply.should == @event + end + end + + it "should sync the property" do + @property.expects(:sync) + + @change.apply + end + + it "should return the default event if syncing the property returns nil" do + @property.stubs(:sync).returns nil + + @change.expects(:event).with(nil).returns @event + + @change.apply.should == @event + end + + it "should return the default event if syncing the property returns an empty array" do + @property.stubs(:sync).returns [] + + @change.expects(:event).with(nil).returns @event + + @change.apply.should == @event + end + + it "should log the change" do + @property.expects(:sync).returns [:one] + + @event.expects(:send_log) + + @change.apply + end + + it "should set the event's message to the change log" do + @property.expects(:change_to_s).returns "my change" + @change.apply.message.should == "my change" + end + + it "should set the event's status to 'success'" do + @change.apply.status.should == "success" + end + + describe "and the change fails" do + before { @property.expects(:sync).raises "an exception" } + + it "should catch the exception and log the err" do + @event.expects(:send_log) + lambda { @change.apply }.should_not raise_error + end + + it "should mark the event status as 'failure'" do + @change.apply.status.should == "failure" + end + + it "should set the event log to a failure log" do + @change.apply.message.should be_include("failed") + end + end + end + end +end diff --git a/spec/unit/transaction/change_spec_spec.rb b/spec/unit/transaction/change_spec_spec.rb deleted file mode 100755 index 9419bbab9..000000000 --- a/spec/unit/transaction/change_spec_spec.rb +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/transaction/change' - -describe Puppet::Transaction::Change do - Change = Puppet::Transaction::Change - - describe "when initializing" do - before do - @property = stub 'property', :path => "/property/path", :should => "shouldval" - end - - it "should require the property and current value" do - lambda { Change.new() }.should raise_error - end - - it "should set its property to the provided property" do - Change.new(@property, "value").property.should == :property - end - - it "should set its 'is' value to the provided value" do - Change.new(@property, "value").is.should == "value" - end - - it "should retrieve the 'should' value from the property" do - # Yay rspec :) - Change.new(@property, "value").should.should == @property.should - end - end - - describe "when an instance" do - before do - @property = stub 'property', :path => "/property/path", :should => "shouldval" - @change = Change.new(@property, "value") - end - - it "should be noop if the property is noop" do - @property.expects(:noop).returns true - @change.noop?.should be_true - end - - it "should be auditing if set so" do - @change.auditing = true - @change.must be_auditing - end - - it "should set its resource to the proxy if it has one" do - @change.proxy = :myresource - @change.resource.should == :myresource - end - - it "should set its resource to the property's resource if no proxy is set" do - @property.expects(:resource).returns :myresource - @change.resource.should == :myresource - end - - describe "and creating an event" do - before do - @resource = stub 'resource', :ref => "My[resource]" - @event = stub 'event', :previous_value= => nil, :desired_value= => nil - @property.stubs(:event).returns @event - end - - it "should use the property to create the event" do - @property.expects(:event).returns @event - @change.event.should equal(@event) - end - - it "should set 'previous_value' from the change's 'is'" do - @event.expects(:previous_value=).with(@change.is) - @change.event - end - - it "should set 'desired_value' from the change's 'should'" do - @event.expects(:desired_value=).with(@change.should) - @change.event - end - end - - describe "and executing" do - before do - @event = Puppet::Transaction::Event.new(:myevent) - @event.stubs(:send_log) - @change.stubs(:noop?).returns false - @property.stubs(:event).returns @event - - @property.stub_everything - @property.stubs(:resource).returns "myresource" - @property.stubs(:name).returns :myprop - end - - describe "in noop mode" do - before { @change.stubs(:noop?).returns true } - - it "should log that it is in noop" do - @property.expects(:is_to_s) - @property.expects(:should_to_s) - - @event.expects(:message=).with { |msg| msg.include?("should be") } - - @change.apply - end - - it "should produce a :noop event and return" do - @property.stub_everything - - @event.expects(:status=).with("noop") - - @change.apply.should == @event - end - end - - describe "in audit mode" do - before { @change.auditing = true } - - it "should log that it is in audit mode" do - @property.expects(:is_to_s) - @property.expects(:should_to_s) - - @event.expects(:message=).with { |msg| msg.include?("audit") } - - @change.apply - end - - it "should produce a :audit event and return" do - @property.stub_everything - - @event.expects(:status=).with("audit") - - @change.apply.should == @event - end - end - - it "should sync the property" do - @property.expects(:sync) - - @change.apply - end - - it "should return the default event if syncing the property returns nil" do - @property.stubs(:sync).returns nil - - @change.expects(:event).with(nil).returns @event - - @change.apply.should == @event - end - - it "should return the default event if syncing the property returns an empty array" do - @property.stubs(:sync).returns [] - - @change.expects(:event).with(nil).returns @event - - @change.apply.should == @event - end - - it "should log the change" do - @property.expects(:sync).returns [:one] - - @event.expects(:send_log) - - @change.apply - end - - it "should set the event's message to the change log" do - @property.expects(:change_to_s).returns "my change" - @change.apply.message.should == "my change" - end - - it "should set the event's status to 'success'" do - @change.apply.status.should == "success" - end - - describe "and the change fails" do - before { @property.expects(:sync).raises "an exception" } - - it "should catch the exception and log the err" do - @event.expects(:send_log) - lambda { @change.apply }.should_not raise_error - end - - it "should mark the event status as 'failure'" do - @change.apply.status.should == "failure" - end - - it "should set the event log to a failure log" do - @change.apply.message.should be_include("failed") - end - end - end - end -end diff --git a/spec/unit/transaction/event_manager_spec.rb b/spec/unit/transaction/event_manager_spec.rb new file mode 100755 index 000000000..6b9fe0345 --- /dev/null +++ b/spec/unit/transaction/event_manager_spec.rb @@ -0,0 +1,260 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/transaction/event_manager' + +describe Puppet::Transaction::EventManager do + describe "at initialization" do + it "should require a transaction" do + Puppet::Transaction::EventManager.new("trans").transaction.should == "trans" + end + end + + it "should delegate its relationship graph to the transaction" do + transaction = stub 'transaction' + manager = Puppet::Transaction::EventManager.new(transaction) + + transaction.expects(:relationship_graph).returns "mygraph" + + manager.relationship_graph.should == "mygraph" + end + + describe "when queueing events" do + before do + @manager = Puppet::Transaction::EventManager.new(@transaction) + + @resource = Puppet::Type.type(:file).new :path => "/my/file" + + @graph = stub 'graph', :matching_edges => [], :resource => @resource + @manager.stubs(:relationship_graph).returns @graph + + @event = Puppet::Transaction::Event.new(:name => :foo, :resource => @resource) + end + + it "should store all of the events in its event list" do + @event2 = Puppet::Transaction::Event.new(:name => :bar, :resource => @resource) + @manager.queue_events(@resource, [@event, @event2]) + + @manager.events.should include(@event) + @manager.events.should include(@event2) + end + + it "should queue events for the target and callback of any matching edges" do + edge1 = stub("edge1", :callback => :c1, :source => stub("s1"), :target => stub("t1", :c1 => nil)) + edge2 = stub("edge2", :callback => :c2, :source => stub("s2"), :target => stub("t2", :c2 => nil)) + + @graph.expects(:matching_edges).with { |event, resource| event == @event }.returns [edge1, edge2] + + @manager.expects(:queue_events_for_resource).with(@resource, edge1.target, edge1.callback, [@event]) + @manager.expects(:queue_events_for_resource).with(@resource, edge2.target, edge2.callback, [@event]) + + @manager.queue_events(@resource, [@event]) + end + + it "should queue events for the changed resource if the resource is self-refreshing and not being deleted" do + @graph.stubs(:matching_edges).returns [] + + @resource.expects(:self_refresh?).returns true + @resource.expects(:deleting?).returns false + @manager.expects(:queue_events_for_resource).with(@resource, @resource, :refresh, [@event]) + + @manager.queue_events(@resource, [@event]) + end + + it "should not queue events for the changed resource if the resource is not self-refreshing" do + @graph.stubs(:matching_edges).returns [] + + @resource.expects(:self_refresh?).returns false + @resource.stubs(:deleting?).returns false + @manager.expects(:queue_events_for_resource).never + + @manager.queue_events(@resource, [@event]) + end + + it "should not queue events for the changed resource if the resource is being deleted" do + @graph.stubs(:matching_edges).returns [] + + @resource.expects(:self_refresh?).returns true + @resource.expects(:deleting?).returns true + @manager.expects(:queue_events_for_resource).never + + @manager.queue_events(@resource, [@event]) + end + + it "should ignore edges that don't have a callback" do + edge1 = stub("edge1", :callback => :nil, :source => stub("s1"), :target => stub("t1", :c1 => nil)) + + @graph.expects(:matching_edges).returns [edge1] + + @manager.expects(:queue_events_for_resource).never + + @manager.queue_events(@resource, [@event]) + end + + it "should ignore targets that don't respond to the callback" do + edge1 = stub("edge1", :callback => :c1, :source => stub("s1"), :target => stub("t1")) + + @graph.expects(:matching_edges).returns [edge1] + + @manager.expects(:queue_events_for_resource).never + + @manager.queue_events(@resource, [@event]) + end + end + + describe "when queueing events for a resource" do + before do + @transaction = stub 'transaction' + @manager = Puppet::Transaction::EventManager.new(@transaction) + end + + it "should do nothing if no events are queued" do + @manager.queued_events(stub("target")) { |callback, events| raise "should never reach this" } + end + + it "should yield the callback and events for each callback" do + target = stub("target") + + 2.times do |i| + @manager.queue_events_for_resource(stub("source", :info => nil), target, "callback#{i}", ["event#{i}"]) + end + + @manager.queued_events(target) { |callback, events| } + end + + it "should use the source to log that it's scheduling a refresh of the target" do + target = stub("target") + source = stub 'source' + source.expects(:info) + + @manager.queue_events_for_resource(source, target, "callback", ["event"]) + + @manager.queued_events(target) { |callback, events| } + end + end + + describe "when processing events for a given resource" do + before do + @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) + @manager = Puppet::Transaction::EventManager.new(@transaction) + @manager.stubs(:queue_events) + + @resource = Puppet::Type.type(:file).new :path => "/my/file" + @event = Puppet::Transaction::Event.new(:name => :event, :resource => @resource) + end + + it "should call the required callback once for each set of associated events" do + @manager.expects(:queued_events).with(@resource).multiple_yields([:callback1, [@event]], [:callback2, [@event]]) + + @resource.expects(:callback1) + @resource.expects(:callback2) + + @manager.process_events(@resource) + end + + it "should set the 'restarted' state on the resource status" do + @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) + + @resource.stubs(:callback1) + + @manager.process_events(@resource) + + @transaction.resource_status(@resource).should be_restarted + end + + it "should queue a 'restarted' event generated by the resource" do + @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) + + @resource.stubs(:callback1) + + @resource.expects(:event).with(:name => :restarted, :status => "success").returns "myevent" + @manager.expects(:queue_events).with(@resource, ["myevent"]) + + @manager.process_events(@resource) + end + + it "should log that it restarted" do + @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) + + @resource.stubs(:callback1) + + @resource.expects(:notice).with { |msg| msg.include?("Triggered 'callback1'") } + + @manager.process_events(@resource) + end + + describe "and the events include a noop event and at least one non-noop event" do + before do + @event.stubs(:status).returns "noop" + @event2 = Puppet::Transaction::Event.new(:name => :event, :resource => @resource) + @event2.status = "success" + @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event, @event2]) + end + + it "should call the callback" do + @resource.expects(:callback1) + + @manager.process_events(@resource) + end + end + + describe "and the events are all noop events" do + before do + @event.stubs(:status).returns "noop" + @resource.stubs(:event).returns(Puppet::Transaction::Event.new) + @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) + end + + it "should log" do + @resource.expects(:notice).with { |msg| msg.include?("Would have triggered 'callback1'") } + + @manager.process_events(@resource) + end + + it "should not call the callback" do + @resource.expects(:callback1).never + + @manager.process_events(@resource) + end + + it "should queue a new noop event generated from the resource" do + event = Puppet::Transaction::Event.new + @resource.expects(:event).with(:status => "noop", :name => :noop_restart).returns event + @manager.expects(:queue_events).with(@resource, [event]) + + @manager.process_events(@resource) + end + end + + describe "and the callback fails" do + before do + @resource.expects(:callback1).raises "a failure" + @resource.stubs(:err) + + @manager.expects(:queued_events).yields(:callback1, [@event]) + end + + it "should log but not fail" do + @resource.expects(:err) + + lambda { @manager.process_events(@resource) }.should_not raise_error + end + + it "should set the 'failed_restarts' state on the resource status" do + @manager.process_events(@resource) + @transaction.resource_status(@resource).should be_failed_to_restart + end + + it "should not queue a 'restarted' event" do + @manager.expects(:queue_events).never + @manager.process_events(@resource) + end + + it "should set the 'restarted' state on the resource status" do + @manager.process_events(@resource) + @transaction.resource_status(@resource).should_not be_restarted + end + end + end +end diff --git a/spec/unit/transaction/event_manager_spec_spec.rb b/spec/unit/transaction/event_manager_spec_spec.rb deleted file mode 100755 index 6b9fe0345..000000000 --- a/spec/unit/transaction/event_manager_spec_spec.rb +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/transaction/event_manager' - -describe Puppet::Transaction::EventManager do - describe "at initialization" do - it "should require a transaction" do - Puppet::Transaction::EventManager.new("trans").transaction.should == "trans" - end - end - - it "should delegate its relationship graph to the transaction" do - transaction = stub 'transaction' - manager = Puppet::Transaction::EventManager.new(transaction) - - transaction.expects(:relationship_graph).returns "mygraph" - - manager.relationship_graph.should == "mygraph" - end - - describe "when queueing events" do - before do - @manager = Puppet::Transaction::EventManager.new(@transaction) - - @resource = Puppet::Type.type(:file).new :path => "/my/file" - - @graph = stub 'graph', :matching_edges => [], :resource => @resource - @manager.stubs(:relationship_graph).returns @graph - - @event = Puppet::Transaction::Event.new(:name => :foo, :resource => @resource) - end - - it "should store all of the events in its event list" do - @event2 = Puppet::Transaction::Event.new(:name => :bar, :resource => @resource) - @manager.queue_events(@resource, [@event, @event2]) - - @manager.events.should include(@event) - @manager.events.should include(@event2) - end - - it "should queue events for the target and callback of any matching edges" do - edge1 = stub("edge1", :callback => :c1, :source => stub("s1"), :target => stub("t1", :c1 => nil)) - edge2 = stub("edge2", :callback => :c2, :source => stub("s2"), :target => stub("t2", :c2 => nil)) - - @graph.expects(:matching_edges).with { |event, resource| event == @event }.returns [edge1, edge2] - - @manager.expects(:queue_events_for_resource).with(@resource, edge1.target, edge1.callback, [@event]) - @manager.expects(:queue_events_for_resource).with(@resource, edge2.target, edge2.callback, [@event]) - - @manager.queue_events(@resource, [@event]) - end - - it "should queue events for the changed resource if the resource is self-refreshing and not being deleted" do - @graph.stubs(:matching_edges).returns [] - - @resource.expects(:self_refresh?).returns true - @resource.expects(:deleting?).returns false - @manager.expects(:queue_events_for_resource).with(@resource, @resource, :refresh, [@event]) - - @manager.queue_events(@resource, [@event]) - end - - it "should not queue events for the changed resource if the resource is not self-refreshing" do - @graph.stubs(:matching_edges).returns [] - - @resource.expects(:self_refresh?).returns false - @resource.stubs(:deleting?).returns false - @manager.expects(:queue_events_for_resource).never - - @manager.queue_events(@resource, [@event]) - end - - it "should not queue events for the changed resource if the resource is being deleted" do - @graph.stubs(:matching_edges).returns [] - - @resource.expects(:self_refresh?).returns true - @resource.expects(:deleting?).returns true - @manager.expects(:queue_events_for_resource).never - - @manager.queue_events(@resource, [@event]) - end - - it "should ignore edges that don't have a callback" do - edge1 = stub("edge1", :callback => :nil, :source => stub("s1"), :target => stub("t1", :c1 => nil)) - - @graph.expects(:matching_edges).returns [edge1] - - @manager.expects(:queue_events_for_resource).never - - @manager.queue_events(@resource, [@event]) - end - - it "should ignore targets that don't respond to the callback" do - edge1 = stub("edge1", :callback => :c1, :source => stub("s1"), :target => stub("t1")) - - @graph.expects(:matching_edges).returns [edge1] - - @manager.expects(:queue_events_for_resource).never - - @manager.queue_events(@resource, [@event]) - end - end - - describe "when queueing events for a resource" do - before do - @transaction = stub 'transaction' - @manager = Puppet::Transaction::EventManager.new(@transaction) - end - - it "should do nothing if no events are queued" do - @manager.queued_events(stub("target")) { |callback, events| raise "should never reach this" } - end - - it "should yield the callback and events for each callback" do - target = stub("target") - - 2.times do |i| - @manager.queue_events_for_resource(stub("source", :info => nil), target, "callback#{i}", ["event#{i}"]) - end - - @manager.queued_events(target) { |callback, events| } - end - - it "should use the source to log that it's scheduling a refresh of the target" do - target = stub("target") - source = stub 'source' - source.expects(:info) - - @manager.queue_events_for_resource(source, target, "callback", ["event"]) - - @manager.queued_events(target) { |callback, events| } - end - end - - describe "when processing events for a given resource" do - before do - @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) - @manager = Puppet::Transaction::EventManager.new(@transaction) - @manager.stubs(:queue_events) - - @resource = Puppet::Type.type(:file).new :path => "/my/file" - @event = Puppet::Transaction::Event.new(:name => :event, :resource => @resource) - end - - it "should call the required callback once for each set of associated events" do - @manager.expects(:queued_events).with(@resource).multiple_yields([:callback1, [@event]], [:callback2, [@event]]) - - @resource.expects(:callback1) - @resource.expects(:callback2) - - @manager.process_events(@resource) - end - - it "should set the 'restarted' state on the resource status" do - @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) - - @resource.stubs(:callback1) - - @manager.process_events(@resource) - - @transaction.resource_status(@resource).should be_restarted - end - - it "should queue a 'restarted' event generated by the resource" do - @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) - - @resource.stubs(:callback1) - - @resource.expects(:event).with(:name => :restarted, :status => "success").returns "myevent" - @manager.expects(:queue_events).with(@resource, ["myevent"]) - - @manager.process_events(@resource) - end - - it "should log that it restarted" do - @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) - - @resource.stubs(:callback1) - - @resource.expects(:notice).with { |msg| msg.include?("Triggered 'callback1'") } - - @manager.process_events(@resource) - end - - describe "and the events include a noop event and at least one non-noop event" do - before do - @event.stubs(:status).returns "noop" - @event2 = Puppet::Transaction::Event.new(:name => :event, :resource => @resource) - @event2.status = "success" - @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event, @event2]) - end - - it "should call the callback" do - @resource.expects(:callback1) - - @manager.process_events(@resource) - end - end - - describe "and the events are all noop events" do - before do - @event.stubs(:status).returns "noop" - @resource.stubs(:event).returns(Puppet::Transaction::Event.new) - @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) - end - - it "should log" do - @resource.expects(:notice).with { |msg| msg.include?("Would have triggered 'callback1'") } - - @manager.process_events(@resource) - end - - it "should not call the callback" do - @resource.expects(:callback1).never - - @manager.process_events(@resource) - end - - it "should queue a new noop event generated from the resource" do - event = Puppet::Transaction::Event.new - @resource.expects(:event).with(:status => "noop", :name => :noop_restart).returns event - @manager.expects(:queue_events).with(@resource, [event]) - - @manager.process_events(@resource) - end - end - - describe "and the callback fails" do - before do - @resource.expects(:callback1).raises "a failure" - @resource.stubs(:err) - - @manager.expects(:queued_events).yields(:callback1, [@event]) - end - - it "should log but not fail" do - @resource.expects(:err) - - lambda { @manager.process_events(@resource) }.should_not raise_error - end - - it "should set the 'failed_restarts' state on the resource status" do - @manager.process_events(@resource) - @transaction.resource_status(@resource).should be_failed_to_restart - end - - it "should not queue a 'restarted' event" do - @manager.expects(:queue_events).never - @manager.process_events(@resource) - end - - it "should set the 'restarted' state on the resource status" do - @manager.process_events(@resource) - @transaction.resource_status(@resource).should_not be_restarted - end - end - end -end diff --git a/spec/unit/transaction/event_spec.rb b/spec/unit/transaction/event_spec.rb new file mode 100755 index 000000000..85811c105 --- /dev/null +++ b/spec/unit/transaction/event_spec.rb @@ -0,0 +1,108 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/transaction/event' + +describe Puppet::Transaction::Event do + [:previous_value, :desired_value, :property, :resource, :name, :message, :node, :version, :file, :line, :tags].each do |attr| + it "should support #{attr}" do + event = Puppet::Transaction::Event.new + event.send(attr.to_s + "=", "foo") + event.send(attr).should == "foo" + end + end + + it "should always convert the property to a string" do + Puppet::Transaction::Event.new(:property => :foo).property.should == "foo" + end + + it "should always convert the resource to a string" do + Puppet::Transaction::Event.new(:resource => :foo).resource.should == "foo" + end + + it "should produce the message when converted to a string" do + event = Puppet::Transaction::Event.new + event.expects(:message).returns "my message" + event.to_s.should == "my message" + end + + it "should support 'status'" do + event = Puppet::Transaction::Event.new + event.status = "success" + event.status.should == "success" + end + + it "should fail if the status is not to 'audit', 'noop', 'success', or 'failure" do + event = Puppet::Transaction::Event.new + lambda { event.status = "foo" }.should raise_error(ArgumentError) + end + + it "should support tags" do + Puppet::Transaction::Event.ancestors.should include(Puppet::Util::Tagging) + end + + it "should create a timestamp at its creation time" do + Puppet::Transaction::Event.new.time.should be_instance_of(Time) + end + + describe "when sending logs" do + before do + Puppet::Util::Log.stubs(:new) + end + + it "should set the level to the resources's log level if the event status is 'success' and a resource is available" do + resource = stub 'resource' + resource.expects(:[]).with(:loglevel).returns :myloglevel + Puppet::Util::Log.expects(:create).with { |args| args[:level] == :myloglevel } + Puppet::Transaction::Event.new(:status => "success", :resource => resource).send_log + end + + it "should set the level to 'notice' if the event status is 'success' and no resource is available" do + Puppet::Util::Log.expects(:new).with { |args| args[:level] == :notice } + Puppet::Transaction::Event.new(:status => "success").send_log + end + + it "should set the level to 'notice' if the event status is 'noop'" do + Puppet::Util::Log.expects(:new).with { |args| args[:level] == :notice } + Puppet::Transaction::Event.new(:status => "noop").send_log + end + + it "should set the level to 'err' if the event status is 'failure'" do + Puppet::Util::Log.expects(:new).with { |args| args[:level] == :err } + Puppet::Transaction::Event.new(:status => "failure").send_log + end + + it "should set the 'message' to the event log" do + Puppet::Util::Log.expects(:new).with { |args| args[:message] == "my message" } + Puppet::Transaction::Event.new(:message => "my message").send_log + end + + it "should set the tags to the event tags" do + Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} } + Puppet::Transaction::Event.new(:tags => %w{one two}).send_log + end + + [:file, :line, :version].each do |attr| + it "should pass the #{attr}" do + Puppet::Util::Log.expects(:new).with { |args| args[attr] == "my val" } + Puppet::Transaction::Event.new(attr => "my val").send_log + end + end + + it "should use the source description as the source if one is set" do + Puppet::Util::Log.expects(:new).with { |args| args[:source] == "/my/param" } + Puppet::Transaction::Event.new(:source_description => "/my/param", :resource => "Foo[bar]", :property => "foo").send_log + end + + it "should use the property as the source if one is available and no source description is set" do + Puppet::Util::Log.expects(:new).with { |args| args[:source] == "foo" } + Puppet::Transaction::Event.new(:resource => "Foo[bar]", :property => "foo").send_log + end + + it "should use the property as the source if one is available and no property or source description is set" do + Puppet::Util::Log.expects(:new).with { |args| args[:source] == "Foo[bar]" } + Puppet::Transaction::Event.new(:resource => "Foo[bar]").send_log + end + end +end diff --git a/spec/unit/transaction/event_spec_spec.rb b/spec/unit/transaction/event_spec_spec.rb deleted file mode 100755 index 85811c105..000000000 --- a/spec/unit/transaction/event_spec_spec.rb +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/transaction/event' - -describe Puppet::Transaction::Event do - [:previous_value, :desired_value, :property, :resource, :name, :message, :node, :version, :file, :line, :tags].each do |attr| - it "should support #{attr}" do - event = Puppet::Transaction::Event.new - event.send(attr.to_s + "=", "foo") - event.send(attr).should == "foo" - end - end - - it "should always convert the property to a string" do - Puppet::Transaction::Event.new(:property => :foo).property.should == "foo" - end - - it "should always convert the resource to a string" do - Puppet::Transaction::Event.new(:resource => :foo).resource.should == "foo" - end - - it "should produce the message when converted to a string" do - event = Puppet::Transaction::Event.new - event.expects(:message).returns "my message" - event.to_s.should == "my message" - end - - it "should support 'status'" do - event = Puppet::Transaction::Event.new - event.status = "success" - event.status.should == "success" - end - - it "should fail if the status is not to 'audit', 'noop', 'success', or 'failure" do - event = Puppet::Transaction::Event.new - lambda { event.status = "foo" }.should raise_error(ArgumentError) - end - - it "should support tags" do - Puppet::Transaction::Event.ancestors.should include(Puppet::Util::Tagging) - end - - it "should create a timestamp at its creation time" do - Puppet::Transaction::Event.new.time.should be_instance_of(Time) - end - - describe "when sending logs" do - before do - Puppet::Util::Log.stubs(:new) - end - - it "should set the level to the resources's log level if the event status is 'success' and a resource is available" do - resource = stub 'resource' - resource.expects(:[]).with(:loglevel).returns :myloglevel - Puppet::Util::Log.expects(:create).with { |args| args[:level] == :myloglevel } - Puppet::Transaction::Event.new(:status => "success", :resource => resource).send_log - end - - it "should set the level to 'notice' if the event status is 'success' and no resource is available" do - Puppet::Util::Log.expects(:new).with { |args| args[:level] == :notice } - Puppet::Transaction::Event.new(:status => "success").send_log - end - - it "should set the level to 'notice' if the event status is 'noop'" do - Puppet::Util::Log.expects(:new).with { |args| args[:level] == :notice } - Puppet::Transaction::Event.new(:status => "noop").send_log - end - - it "should set the level to 'err' if the event status is 'failure'" do - Puppet::Util::Log.expects(:new).with { |args| args[:level] == :err } - Puppet::Transaction::Event.new(:status => "failure").send_log - end - - it "should set the 'message' to the event log" do - Puppet::Util::Log.expects(:new).with { |args| args[:message] == "my message" } - Puppet::Transaction::Event.new(:message => "my message").send_log - end - - it "should set the tags to the event tags" do - Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} } - Puppet::Transaction::Event.new(:tags => %w{one two}).send_log - end - - [:file, :line, :version].each do |attr| - it "should pass the #{attr}" do - Puppet::Util::Log.expects(:new).with { |args| args[attr] == "my val" } - Puppet::Transaction::Event.new(attr => "my val").send_log - end - end - - it "should use the source description as the source if one is set" do - Puppet::Util::Log.expects(:new).with { |args| args[:source] == "/my/param" } - Puppet::Transaction::Event.new(:source_description => "/my/param", :resource => "Foo[bar]", :property => "foo").send_log - end - - it "should use the property as the source if one is available and no source description is set" do - Puppet::Util::Log.expects(:new).with { |args| args[:source] == "foo" } - Puppet::Transaction::Event.new(:resource => "Foo[bar]", :property => "foo").send_log - end - - it "should use the property as the source if one is available and no property or source description is set" do - Puppet::Util::Log.expects(:new).with { |args| args[:source] == "Foo[bar]" } - Puppet::Transaction::Event.new(:resource => "Foo[bar]").send_log - end - end -end diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb new file mode 100755 index 000000000..4f3c90d88 --- /dev/null +++ b/spec/unit/transaction/report_spec.rb @@ -0,0 +1,230 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/transaction/report' + +describe Puppet::Transaction::Report do + it "should set its host name to the certname" do + Puppet.settings.expects(:value).with(:certname).returns "myhost" + Puppet::Transaction::Report.new.host.should == "myhost" + end + + it "should return its host name as its name" do + r = Puppet::Transaction::Report.new + r.name.should == r.host + end + + it "should create an initialization timestamp" do + Time.expects(:now).returns "mytime" + Puppet::Transaction::Report.new.time.should == "mytime" + end + + describe "when accepting logs" do + before do + @report = Puppet::Transaction::Report.new + end + + it "should add new logs to the log list" do + @report << "log" + @report.logs[-1].should == "log" + end + + it "should return self" do + r = @report << "log" + r.should equal(@report) + end + end + + describe "when accepting resource statuses" do + before do + @report = Puppet::Transaction::Report.new + end + + it "should add each status to its status list" do + status = stub 'status', :resource => "foo" + @report.add_resource_status status + @report.resource_statuses["foo"].should equal(status) + end + end + + describe "when using the indirector" do + it "should redirect :find to the indirection" do + @indirection = stub 'indirection', :name => :report + Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) + @indirection.expects(:find) + Puppet::Transaction::Report.find(:report) + end + + it "should redirect :save to the indirection" do + Facter.stubs(:value).returns("eh") + @indirection = stub 'indirection', :name => :report + Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) + report = Puppet::Transaction::Report.new + @indirection.expects(:save) + report.save + end + + it "should default to the 'processor' terminus" do + Puppet::Transaction::Report.indirection.terminus_class.should == :processor + end + + it "should delegate its name attribute to its host method" do + report = Puppet::Transaction::Report.new + report.expects(:host).returns "me" + report.name.should == "me" + end + + after do + Puppet::Util::Cacher.expire + end + end + + describe "when computing exit status" do + it "should produce 2 if changes are present" do + report = Puppet::Transaction::Report.new + report.add_metric("changes", {:total => 1}) + report.add_metric("resources", {:failed => 0}) + report.exit_status.should == 2 + end + + it "should produce 4 if failures are present" do + report = Puppet::Transaction::Report.new + report.add_metric("changes", {:total => 0}) + report.add_metric("resources", {:failed => 1}) + report.exit_status.should == 4 + end + + it "should produce 6 if both changes and failures are present" do + report = Puppet::Transaction::Report.new + report.add_metric("changes", {:total => 1}) + report.add_metric("resources", {:failed => 1}) + report.exit_status.should == 6 + end + end + + describe "when calculating metrics" do + before do + @report = Puppet::Transaction::Report.new + end + + def metric(name, value) + if metric = @report.metrics[name.to_s] + metric[value] + else + nil + end + end + + def add_statuses(count, type = :file) + 3.times do |i| + status = Puppet::Resource::Status.new(Puppet::Type.type(type).new(:title => "/my/path#{i}")) + yield status if block_given? + @report.add_resource_status status + end + end + + + [:time, :resources, :changes, :events].each do |type| + it "should add #{type} metrics" do + @report.calculate_metrics + @report.metrics[type.to_s].should be_instance_of(Puppet::Transaction::Metric) + end + end + + describe "for resources" do + it "should provide the total number of resources" do + add_statuses(3) + + @report.calculate_metrics + metric(:resources, :total).should == 3 + end + + Puppet::Resource::Status::STATES.each do |state| + it "should provide the number of #{state} resources as determined by the status objects" do + add_statuses(3) { |status| status.send(state.to_s + "=", true) } + + @report.calculate_metrics + metric(:resources, state).should == 3 + end + end + end + + describe "for changes" do + it "should provide the number of changes from the resource statuses" do + add_statuses(3) { |status| status.change_count = 3 } + @report.calculate_metrics + metric(:changes, :total).should == 9 + end + end + + describe "for times" do + it "should provide the total amount of time for each resource type" do + add_statuses(3, :file) do |status| + status.evaluation_time = 1 + end + add_statuses(3, :exec) do |status| + status.evaluation_time = 2 + end + add_statuses(3, :mount) do |status| + status.evaluation_time = 3 + end + + @report.calculate_metrics + + metric(:time, "file").should == 3 + metric(:time, "exec").should == 6 + metric(:time, "mount").should == 9 + end + + it "should add any provided times from external sources" do + @report.add_times :foobar, 50 + @report.calculate_metrics + metric(:time, "foobar").should == 50 + end + end + + describe "for events" do + it "should provide the total number of events" do + add_statuses(3) do |status| + 3.times { |i| status.add_event(Puppet::Transaction::Event.new) } + end + @report.calculate_metrics + metric(:events, :total).should == 9 + end + + Puppet::Transaction::Event::EVENT_STATUSES.each do |status_name| + it "should provide the number of #{status_name} events" do + add_statuses(3) do |status| + 3.times do |i| + event = Puppet::Transaction::Event.new + event.status = status_name + status.add_event(event) + end + end + + @report.calculate_metrics + metric(:events, status_name).should == 9 + end + end + end + end + + describe "when producing a summary" do + before do + resource = Puppet::Type.type(:notify).new(:name => "testing") + catalog = Puppet::Resource::Catalog.new + catalog.add_resource resource + trans = catalog.apply + + @report = trans.report + @report.calculate_metrics + end + + %w{Changes Total Resources}.each do |main| + it "should include information on #{main} in the summary" do + @report.summary.should be_include(main) + end + end + end +end diff --git a/spec/unit/transaction/report_spec_spec.rb b/spec/unit/transaction/report_spec_spec.rb deleted file mode 100755 index 4f3c90d88..000000000 --- a/spec/unit/transaction/report_spec_spec.rb +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/transaction/report' - -describe Puppet::Transaction::Report do - it "should set its host name to the certname" do - Puppet.settings.expects(:value).with(:certname).returns "myhost" - Puppet::Transaction::Report.new.host.should == "myhost" - end - - it "should return its host name as its name" do - r = Puppet::Transaction::Report.new - r.name.should == r.host - end - - it "should create an initialization timestamp" do - Time.expects(:now).returns "mytime" - Puppet::Transaction::Report.new.time.should == "mytime" - end - - describe "when accepting logs" do - before do - @report = Puppet::Transaction::Report.new - end - - it "should add new logs to the log list" do - @report << "log" - @report.logs[-1].should == "log" - end - - it "should return self" do - r = @report << "log" - r.should equal(@report) - end - end - - describe "when accepting resource statuses" do - before do - @report = Puppet::Transaction::Report.new - end - - it "should add each status to its status list" do - status = stub 'status', :resource => "foo" - @report.add_resource_status status - @report.resource_statuses["foo"].should equal(status) - end - end - - describe "when using the indirector" do - it "should redirect :find to the indirection" do - @indirection = stub 'indirection', :name => :report - Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) - @indirection.expects(:find) - Puppet::Transaction::Report.find(:report) - end - - it "should redirect :save to the indirection" do - Facter.stubs(:value).returns("eh") - @indirection = stub 'indirection', :name => :report - Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) - report = Puppet::Transaction::Report.new - @indirection.expects(:save) - report.save - end - - it "should default to the 'processor' terminus" do - Puppet::Transaction::Report.indirection.terminus_class.should == :processor - end - - it "should delegate its name attribute to its host method" do - report = Puppet::Transaction::Report.new - report.expects(:host).returns "me" - report.name.should == "me" - end - - after do - Puppet::Util::Cacher.expire - end - end - - describe "when computing exit status" do - it "should produce 2 if changes are present" do - report = Puppet::Transaction::Report.new - report.add_metric("changes", {:total => 1}) - report.add_metric("resources", {:failed => 0}) - report.exit_status.should == 2 - end - - it "should produce 4 if failures are present" do - report = Puppet::Transaction::Report.new - report.add_metric("changes", {:total => 0}) - report.add_metric("resources", {:failed => 1}) - report.exit_status.should == 4 - end - - it "should produce 6 if both changes and failures are present" do - report = Puppet::Transaction::Report.new - report.add_metric("changes", {:total => 1}) - report.add_metric("resources", {:failed => 1}) - report.exit_status.should == 6 - end - end - - describe "when calculating metrics" do - before do - @report = Puppet::Transaction::Report.new - end - - def metric(name, value) - if metric = @report.metrics[name.to_s] - metric[value] - else - nil - end - end - - def add_statuses(count, type = :file) - 3.times do |i| - status = Puppet::Resource::Status.new(Puppet::Type.type(type).new(:title => "/my/path#{i}")) - yield status if block_given? - @report.add_resource_status status - end - end - - - [:time, :resources, :changes, :events].each do |type| - it "should add #{type} metrics" do - @report.calculate_metrics - @report.metrics[type.to_s].should be_instance_of(Puppet::Transaction::Metric) - end - end - - describe "for resources" do - it "should provide the total number of resources" do - add_statuses(3) - - @report.calculate_metrics - metric(:resources, :total).should == 3 - end - - Puppet::Resource::Status::STATES.each do |state| - it "should provide the number of #{state} resources as determined by the status objects" do - add_statuses(3) { |status| status.send(state.to_s + "=", true) } - - @report.calculate_metrics - metric(:resources, state).should == 3 - end - end - end - - describe "for changes" do - it "should provide the number of changes from the resource statuses" do - add_statuses(3) { |status| status.change_count = 3 } - @report.calculate_metrics - metric(:changes, :total).should == 9 - end - end - - describe "for times" do - it "should provide the total amount of time for each resource type" do - add_statuses(3, :file) do |status| - status.evaluation_time = 1 - end - add_statuses(3, :exec) do |status| - status.evaluation_time = 2 - end - add_statuses(3, :mount) do |status| - status.evaluation_time = 3 - end - - @report.calculate_metrics - - metric(:time, "file").should == 3 - metric(:time, "exec").should == 6 - metric(:time, "mount").should == 9 - end - - it "should add any provided times from external sources" do - @report.add_times :foobar, 50 - @report.calculate_metrics - metric(:time, "foobar").should == 50 - end - end - - describe "for events" do - it "should provide the total number of events" do - add_statuses(3) do |status| - 3.times { |i| status.add_event(Puppet::Transaction::Event.new) } - end - @report.calculate_metrics - metric(:events, :total).should == 9 - end - - Puppet::Transaction::Event::EVENT_STATUSES.each do |status_name| - it "should provide the number of #{status_name} events" do - add_statuses(3) do |status| - 3.times do |i| - event = Puppet::Transaction::Event.new - event.status = status_name - status.add_event(event) - end - end - - @report.calculate_metrics - metric(:events, status_name).should == 9 - end - end - end - end - - describe "when producing a summary" do - before do - resource = Puppet::Type.type(:notify).new(:name => "testing") - catalog = Puppet::Resource::Catalog.new - catalog.add_resource resource - trans = catalog.apply - - @report = trans.report - @report.calculate_metrics - end - - %w{Changes Total Resources}.each do |main| - it "should include information on #{main} in the summary" do - @report.summary.should be_include(main) - end - end - end -end diff --git a/spec/unit/transaction/resource_harness_spec.rb b/spec/unit/transaction/resource_harness_spec.rb new file mode 100755 index 000000000..cbb796cde --- /dev/null +++ b/spec/unit/transaction/resource_harness_spec.rb @@ -0,0 +1,401 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/transaction/resource_harness' + +describe Puppet::Transaction::ResourceHarness do + before do + @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) + @resource = Puppet::Type.type(:file).new :path => "/my/file" + @harness = Puppet::Transaction::ResourceHarness.new(@transaction) + @current_state = Puppet::Resource.new(:file, "/my/file") + @resource.stubs(:retrieve).returns @current_state + @status = Puppet::Resource::Status.new(@resource) + Puppet::Resource::Status.stubs(:new).returns @status + end + + it "should accept a transaction at initialization" do + harness = Puppet::Transaction::ResourceHarness.new(@transaction) + harness.transaction.should equal(@transaction) + end + + it "should delegate to the transaction for its relationship graph" do + @transaction.expects(:relationship_graph).returns "relgraph" + Puppet::Transaction::ResourceHarness.new(@transaction).relationship_graph.should == "relgraph" + end + + describe "when copying audited parameters" do + before do + @resource = Puppet::Type.type(:file).new :path => "/foo/bar", :audit => :mode + end + + it "should do nothing if no parameters are being audited" do + @resource[:audit] = [] + @harness.expects(:cached).never + @harness.copy_audited_parameters(@resource, {}).should == [] + end + + it "should do nothing if an audited parameter already has a desired value set" do + @resource[:mode] = "755" + @harness.expects(:cached).never + @harness.copy_audited_parameters(@resource, {}).should == [] + end + + it "should copy any cached values to the 'should' values" do + @harness.cache(@resource, :mode, "755") + @harness.copy_audited_parameters(@resource, {}).should == [:mode] + + @resource[:mode].should == 0755 + end + + it "should cache and log the current value if no cached values are present" do + @resource.expects(:notice) + @harness.copy_audited_parameters(@resource, {:mode => "755"}).should == [] + + @harness.cached(@resource, :mode).should == "755" + end + end + + describe "when evaluating a resource" do + it "should create and return a resource status instance for the resource" do + @harness.evaluate(@resource).should be_instance_of(Puppet::Resource::Status) + end + + it "should fail if no status can be created" do + Puppet::Resource::Status.expects(:new).raises ArgumentError + + lambda { @harness.evaluate(@resource) }.should raise_error + end + + it "should retrieve the current state of the resource" do + @resource.expects(:retrieve).returns @current_state + @harness.evaluate(@resource) + end + + it "should mark the resource as failed and return if the current state cannot be retrieved" do + @resource.expects(:retrieve).raises ArgumentError + @harness.evaluate(@resource).should be_failed + end + + it "should use the status and retrieved state to determine which changes need to be made" do + @harness.expects(:changes_to_perform).with(@status, @resource).returns [] + @harness.evaluate(@resource) + end + + it "should mark the status as out of sync and apply the created changes if there are any" do + changes = %w{mychanges} + @harness.expects(:changes_to_perform).returns changes + @harness.expects(:apply_changes).with(@status, changes) + @harness.evaluate(@resource).should be_out_of_sync + end + + it "should cache the last-synced time" do + changes = %w{mychanges} + @harness.stubs(:changes_to_perform).returns changes + @harness.stubs(:apply_changes) + @harness.expects(:cache).with { |resource, name, time| name == :synced and time.is_a?(Time) } + @harness.evaluate(@resource) + end + + it "should flush the resource when applying changes if appropriate" do + changes = %w{mychanges} + @harness.stubs(:changes_to_perform).returns changes + @harness.stubs(:apply_changes) + @resource.expects(:flush) + @harness.evaluate(@resource) + end + + it "should use the status and retrieved state to determine which changes need to be made" do + @harness.expects(:changes_to_perform).with(@status, @resource).returns [] + @harness.evaluate(@resource) + end + + it "should not attempt to apply changes if none need to be made" do + @harness.expects(:changes_to_perform).returns [] + @harness.expects(:apply_changes).never + @harness.evaluate(@resource).should_not be_out_of_sync + end + + it "should store the resource's evaluation time in the resource status" do + @harness.evaluate(@resource).evaluation_time.should be_instance_of(Float) + end + + it "should set the change count to the total number of changes" do + changes = %w{a b c d} + @harness.expects(:changes_to_perform).returns changes + @harness.expects(:apply_changes).with(@status, changes) + @harness.evaluate(@resource).change_count.should == 4 + end + end + + describe "when creating changes" do + before do + @current_state = Puppet::Resource.new(:file, "/my/file") + @resource.stubs(:retrieve).returns @current_state + Puppet.features.stubs(:root?).returns true + end + + it "should retrieve the current values from the resource" do + @resource.expects(:retrieve).returns @current_state + @harness.changes_to_perform(@status, @resource) + end + + it "should cache that the resource was checked" do + @harness.expects(:cache).with { |resource, name, time| name == :checked and time.is_a?(Time) } + @harness.changes_to_perform(@status, @resource) + end + + it "should create changes with the appropriate property and current value" do + @resource[:ensure] = :present + @current_state[:ensure] = :absent + + change = stub 'change' + Puppet::Transaction::Change.expects(:new).with(@resource.parameter(:ensure), :absent).returns change + + @harness.changes_to_perform(@status, @resource)[0].should equal(change) + end + + it "should not attempt to manage properties that do not have desired values set" do + mode = @resource.newattr(:mode) + @current_state[:mode] = :absent + + mode.expects(:insync?).never + + @harness.changes_to_perform(@status, @resource) + end + + it "should copy audited parameters" do + @resource[:audit] = :mode + @harness.cache(@resource, :mode, "755") + @harness.changes_to_perform(@status, @resource) + @resource[:mode].should == 0755 + end + + it "should mark changes created as a result of auditing as auditing changes" do + @current_state[:mode] = 0644 + @resource[:audit] = :mode + @harness.cache(@resource, :mode, "755") + @harness.changes_to_perform(@status, @resource)[0].must be_auditing + end + + describe "and the 'ensure' parameter is present but not in sync" do + it "should return a single change for the 'ensure' parameter" do + @resource[:ensure] = :present + @resource[:mode] = "755" + @current_state[:ensure] = :absent + @current_state[:mode] = :absent + + @resource.stubs(:retrieve).returns @current_state + + changes = @harness.changes_to_perform(@status, @resource) + changes.length.should == 1 + changes[0].property.name.should == :ensure + end + end + + describe "and the 'ensure' parameter should be set to 'absent', and is correctly set to 'absent'" do + it "should return no changes" do + @resource[:ensure] = :absent + @resource[:mode] = "755" + @current_state[:ensure] = :absent + @current_state[:mode] = :absent + + @harness.changes_to_perform(@status, @resource).should == [] + end + end + + describe "and the 'ensure' parameter is 'absent' and there is no 'desired value'" do + it "should return no changes" do + @resource.newattr(:ensure) + @resource[:mode] = "755" + @current_state[:ensure] = :absent + @current_state[:mode] = :absent + + @harness.changes_to_perform(@status, @resource).should == [] + end + end + + describe "and non-'ensure' parameters are not in sync" do + it "should return a change for each parameter that is not in sync" do + @resource[:ensure] = :present + @resource[:mode] = "755" + @resource[:owner] = 0 + @current_state[:ensure] = :present + @current_state[:mode] = 0444 + @current_state[:owner] = 50 + + mode = stub 'mode_change' + owner = stub 'owner_change' + Puppet::Transaction::Change.expects(:new).with(@resource.parameter(:mode), 0444).returns mode + Puppet::Transaction::Change.expects(:new).with(@resource.parameter(:owner), 50).returns owner + + changes = @harness.changes_to_perform(@status, @resource) + changes.length.should == 2 + changes.should be_include(mode) + changes.should be_include(owner) + end + end + + describe "and all parameters are in sync" do + it "should return an empty array" do + @resource[:ensure] = :present + @resource[:mode] = "755" + @current_state[:ensure] = :present + @current_state[:mode] = 0755 + @harness.changes_to_perform(@status, @resource).should == [] + end + end + end + + describe "when applying changes" do + before do + @change1 = stub 'change1', :apply => stub("event", :status => "success"), :auditing? => false + @change2 = stub 'change2', :apply => stub("event", :status => "success"), :auditing? => false + @changes = [@change1, @change2] + end + + it "should apply the change" do + @change1.expects(:apply).returns( stub("event", :status => "success") ) + @change2.expects(:apply).returns( stub("event", :status => "success") ) + + @harness.apply_changes(@status, @changes) + end + + it "should mark the resource as changed" do + @harness.apply_changes(@status, @changes) + + @status.should be_changed + end + + it "should queue the resulting event" do + @harness.apply_changes(@status, @changes) + + @status.events.should be_include(@change1.apply) + @status.events.should be_include(@change2.apply) + end + + it "should cache the new value if it is an auditing change" do + @change1.expects(:auditing?).returns true + property = stub 'property', :name => "foo", :resource => "myres" + @change1.stubs(:property).returns property + @change1.stubs(:is).returns "myval" + + @harness.apply_changes(@status, @changes) + + @harness.cached("myres", "foo").should == "myval" + end + end + + describe "when determining whether the resource can be changed" do + before do + @resource.stubs(:purging?).returns true + @resource.stubs(:deleting?).returns true + end + + it "should be true if the resource is not being purged" do + @resource.expects(:purging?).returns false + @harness.should be_allow_changes(@resource) + end + + it "should be true if the resource is not being deleted" do + @resource.expects(:deleting?).returns false + @harness.should be_allow_changes(@resource) + end + + it "should be true if the resource has no dependents" do + @harness.relationship_graph.expects(:dependents).with(@resource).returns [] + @harness.should be_allow_changes(@resource) + end + + it "should be true if all dependents are being deleted" do + dep = stub 'dependent', :deleting? => true + @harness.relationship_graph.expects(:dependents).with(@resource).returns [dep] + @resource.expects(:purging?).returns true + @harness.should be_allow_changes(@resource) + end + + it "should be false if the resource's dependents are not being deleted" do + dep = stub 'dependent', :deleting? => false, :ref => "myres" + @resource.expects(:warning) + @harness.relationship_graph.expects(:dependents).with(@resource).returns [dep] + @harness.should_not be_allow_changes(@resource) + end + end + + describe "when finding the schedule" do + before do + @catalog = Puppet::Resource::Catalog.new + @resource.catalog = @catalog + end + + it "should warn and return nil if the resource has no catalog" do + @resource.catalog = nil + @resource.expects(:warning) + + @harness.schedule(@resource).should be_nil + end + + it "should return nil if the resource specifies no schedule" do + @harness.schedule(@resource).should be_nil + end + + it "should fail if the named schedule cannot be found" do + @resource[:schedule] = "whatever" + @resource.expects(:fail) + @harness.schedule(@resource) + end + + it "should return the named schedule if it exists" do + sched = Puppet::Type.type(:schedule).new(:name => "sched") + @catalog.add_resource(sched) + @resource[:schedule] = "sched" + @harness.schedule(@resource).to_s.should == sched.to_s + end + end + + describe "when determining if a resource is scheduled" do + before do + @catalog = Puppet::Resource::Catalog.new + @resource.catalog = @catalog + @status = Puppet::Resource::Status.new(@resource) + end + + it "should return true if 'ignoreschedules' is set" do + Puppet[:ignoreschedules] = true + @resource[:schedule] = "meh" + @harness.should be_scheduled(@status, @resource) + end + + it "should return true if the resource has no schedule set" do + @harness.should be_scheduled(@status, @resource) + end + + it "should return the result of matching the schedule with the cached 'checked' time if a schedule is set" do + t = Time.now + @harness.expects(:cached).with(@resource, :checked).returns(t) + + sched = Puppet::Type.type(:schedule).new(:name => "sched") + @catalog.add_resource(sched) + @resource[:schedule] = "sched" + + sched.expects(:match?).with(t.to_i).returns "feh" + + @harness.scheduled?(@status, @resource).should == "feh" + end + end + + it "should be able to cache data in the Storage module" do + data = {} + Puppet::Util::Storage.expects(:cache).with(@resource).returns data + @harness.cache(@resource, :foo, "something") + + data[:foo].should == "something" + end + + it "should be able to retrieve data from the cache" do + data = {:foo => "other"} + Puppet::Util::Storage.expects(:cache).with(@resource).returns data + @harness.cached(@resource, :foo).should == "other" + end +end diff --git a/spec/unit/transaction/resource_harness_spec_spec.rb b/spec/unit/transaction/resource_harness_spec_spec.rb deleted file mode 100755 index cbb796cde..000000000 --- a/spec/unit/transaction/resource_harness_spec_spec.rb +++ /dev/null @@ -1,401 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/transaction/resource_harness' - -describe Puppet::Transaction::ResourceHarness do - before do - @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) - @resource = Puppet::Type.type(:file).new :path => "/my/file" - @harness = Puppet::Transaction::ResourceHarness.new(@transaction) - @current_state = Puppet::Resource.new(:file, "/my/file") - @resource.stubs(:retrieve).returns @current_state - @status = Puppet::Resource::Status.new(@resource) - Puppet::Resource::Status.stubs(:new).returns @status - end - - it "should accept a transaction at initialization" do - harness = Puppet::Transaction::ResourceHarness.new(@transaction) - harness.transaction.should equal(@transaction) - end - - it "should delegate to the transaction for its relationship graph" do - @transaction.expects(:relationship_graph).returns "relgraph" - Puppet::Transaction::ResourceHarness.new(@transaction).relationship_graph.should == "relgraph" - end - - describe "when copying audited parameters" do - before do - @resource = Puppet::Type.type(:file).new :path => "/foo/bar", :audit => :mode - end - - it "should do nothing if no parameters are being audited" do - @resource[:audit] = [] - @harness.expects(:cached).never - @harness.copy_audited_parameters(@resource, {}).should == [] - end - - it "should do nothing if an audited parameter already has a desired value set" do - @resource[:mode] = "755" - @harness.expects(:cached).never - @harness.copy_audited_parameters(@resource, {}).should == [] - end - - it "should copy any cached values to the 'should' values" do - @harness.cache(@resource, :mode, "755") - @harness.copy_audited_parameters(@resource, {}).should == [:mode] - - @resource[:mode].should == 0755 - end - - it "should cache and log the current value if no cached values are present" do - @resource.expects(:notice) - @harness.copy_audited_parameters(@resource, {:mode => "755"}).should == [] - - @harness.cached(@resource, :mode).should == "755" - end - end - - describe "when evaluating a resource" do - it "should create and return a resource status instance for the resource" do - @harness.evaluate(@resource).should be_instance_of(Puppet::Resource::Status) - end - - it "should fail if no status can be created" do - Puppet::Resource::Status.expects(:new).raises ArgumentError - - lambda { @harness.evaluate(@resource) }.should raise_error - end - - it "should retrieve the current state of the resource" do - @resource.expects(:retrieve).returns @current_state - @harness.evaluate(@resource) - end - - it "should mark the resource as failed and return if the current state cannot be retrieved" do - @resource.expects(:retrieve).raises ArgumentError - @harness.evaluate(@resource).should be_failed - end - - it "should use the status and retrieved state to determine which changes need to be made" do - @harness.expects(:changes_to_perform).with(@status, @resource).returns [] - @harness.evaluate(@resource) - end - - it "should mark the status as out of sync and apply the created changes if there are any" do - changes = %w{mychanges} - @harness.expects(:changes_to_perform).returns changes - @harness.expects(:apply_changes).with(@status, changes) - @harness.evaluate(@resource).should be_out_of_sync - end - - it "should cache the last-synced time" do - changes = %w{mychanges} - @harness.stubs(:changes_to_perform).returns changes - @harness.stubs(:apply_changes) - @harness.expects(:cache).with { |resource, name, time| name == :synced and time.is_a?(Time) } - @harness.evaluate(@resource) - end - - it "should flush the resource when applying changes if appropriate" do - changes = %w{mychanges} - @harness.stubs(:changes_to_perform).returns changes - @harness.stubs(:apply_changes) - @resource.expects(:flush) - @harness.evaluate(@resource) - end - - it "should use the status and retrieved state to determine which changes need to be made" do - @harness.expects(:changes_to_perform).with(@status, @resource).returns [] - @harness.evaluate(@resource) - end - - it "should not attempt to apply changes if none need to be made" do - @harness.expects(:changes_to_perform).returns [] - @harness.expects(:apply_changes).never - @harness.evaluate(@resource).should_not be_out_of_sync - end - - it "should store the resource's evaluation time in the resource status" do - @harness.evaluate(@resource).evaluation_time.should be_instance_of(Float) - end - - it "should set the change count to the total number of changes" do - changes = %w{a b c d} - @harness.expects(:changes_to_perform).returns changes - @harness.expects(:apply_changes).with(@status, changes) - @harness.evaluate(@resource).change_count.should == 4 - end - end - - describe "when creating changes" do - before do - @current_state = Puppet::Resource.new(:file, "/my/file") - @resource.stubs(:retrieve).returns @current_state - Puppet.features.stubs(:root?).returns true - end - - it "should retrieve the current values from the resource" do - @resource.expects(:retrieve).returns @current_state - @harness.changes_to_perform(@status, @resource) - end - - it "should cache that the resource was checked" do - @harness.expects(:cache).with { |resource, name, time| name == :checked and time.is_a?(Time) } - @harness.changes_to_perform(@status, @resource) - end - - it "should create changes with the appropriate property and current value" do - @resource[:ensure] = :present - @current_state[:ensure] = :absent - - change = stub 'change' - Puppet::Transaction::Change.expects(:new).with(@resource.parameter(:ensure), :absent).returns change - - @harness.changes_to_perform(@status, @resource)[0].should equal(change) - end - - it "should not attempt to manage properties that do not have desired values set" do - mode = @resource.newattr(:mode) - @current_state[:mode] = :absent - - mode.expects(:insync?).never - - @harness.changes_to_perform(@status, @resource) - end - - it "should copy audited parameters" do - @resource[:audit] = :mode - @harness.cache(@resource, :mode, "755") - @harness.changes_to_perform(@status, @resource) - @resource[:mode].should == 0755 - end - - it "should mark changes created as a result of auditing as auditing changes" do - @current_state[:mode] = 0644 - @resource[:audit] = :mode - @harness.cache(@resource, :mode, "755") - @harness.changes_to_perform(@status, @resource)[0].must be_auditing - end - - describe "and the 'ensure' parameter is present but not in sync" do - it "should return a single change for the 'ensure' parameter" do - @resource[:ensure] = :present - @resource[:mode] = "755" - @current_state[:ensure] = :absent - @current_state[:mode] = :absent - - @resource.stubs(:retrieve).returns @current_state - - changes = @harness.changes_to_perform(@status, @resource) - changes.length.should == 1 - changes[0].property.name.should == :ensure - end - end - - describe "and the 'ensure' parameter should be set to 'absent', and is correctly set to 'absent'" do - it "should return no changes" do - @resource[:ensure] = :absent - @resource[:mode] = "755" - @current_state[:ensure] = :absent - @current_state[:mode] = :absent - - @harness.changes_to_perform(@status, @resource).should == [] - end - end - - describe "and the 'ensure' parameter is 'absent' and there is no 'desired value'" do - it "should return no changes" do - @resource.newattr(:ensure) - @resource[:mode] = "755" - @current_state[:ensure] = :absent - @current_state[:mode] = :absent - - @harness.changes_to_perform(@status, @resource).should == [] - end - end - - describe "and non-'ensure' parameters are not in sync" do - it "should return a change for each parameter that is not in sync" do - @resource[:ensure] = :present - @resource[:mode] = "755" - @resource[:owner] = 0 - @current_state[:ensure] = :present - @current_state[:mode] = 0444 - @current_state[:owner] = 50 - - mode = stub 'mode_change' - owner = stub 'owner_change' - Puppet::Transaction::Change.expects(:new).with(@resource.parameter(:mode), 0444).returns mode - Puppet::Transaction::Change.expects(:new).with(@resource.parameter(:owner), 50).returns owner - - changes = @harness.changes_to_perform(@status, @resource) - changes.length.should == 2 - changes.should be_include(mode) - changes.should be_include(owner) - end - end - - describe "and all parameters are in sync" do - it "should return an empty array" do - @resource[:ensure] = :present - @resource[:mode] = "755" - @current_state[:ensure] = :present - @current_state[:mode] = 0755 - @harness.changes_to_perform(@status, @resource).should == [] - end - end - end - - describe "when applying changes" do - before do - @change1 = stub 'change1', :apply => stub("event", :status => "success"), :auditing? => false - @change2 = stub 'change2', :apply => stub("event", :status => "success"), :auditing? => false - @changes = [@change1, @change2] - end - - it "should apply the change" do - @change1.expects(:apply).returns( stub("event", :status => "success") ) - @change2.expects(:apply).returns( stub("event", :status => "success") ) - - @harness.apply_changes(@status, @changes) - end - - it "should mark the resource as changed" do - @harness.apply_changes(@status, @changes) - - @status.should be_changed - end - - it "should queue the resulting event" do - @harness.apply_changes(@status, @changes) - - @status.events.should be_include(@change1.apply) - @status.events.should be_include(@change2.apply) - end - - it "should cache the new value if it is an auditing change" do - @change1.expects(:auditing?).returns true - property = stub 'property', :name => "foo", :resource => "myres" - @change1.stubs(:property).returns property - @change1.stubs(:is).returns "myval" - - @harness.apply_changes(@status, @changes) - - @harness.cached("myres", "foo").should == "myval" - end - end - - describe "when determining whether the resource can be changed" do - before do - @resource.stubs(:purging?).returns true - @resource.stubs(:deleting?).returns true - end - - it "should be true if the resource is not being purged" do - @resource.expects(:purging?).returns false - @harness.should be_allow_changes(@resource) - end - - it "should be true if the resource is not being deleted" do - @resource.expects(:deleting?).returns false - @harness.should be_allow_changes(@resource) - end - - it "should be true if the resource has no dependents" do - @harness.relationship_graph.expects(:dependents).with(@resource).returns [] - @harness.should be_allow_changes(@resource) - end - - it "should be true if all dependents are being deleted" do - dep = stub 'dependent', :deleting? => true - @harness.relationship_graph.expects(:dependents).with(@resource).returns [dep] - @resource.expects(:purging?).returns true - @harness.should be_allow_changes(@resource) - end - - it "should be false if the resource's dependents are not being deleted" do - dep = stub 'dependent', :deleting? => false, :ref => "myres" - @resource.expects(:warning) - @harness.relationship_graph.expects(:dependents).with(@resource).returns [dep] - @harness.should_not be_allow_changes(@resource) - end - end - - describe "when finding the schedule" do - before do - @catalog = Puppet::Resource::Catalog.new - @resource.catalog = @catalog - end - - it "should warn and return nil if the resource has no catalog" do - @resource.catalog = nil - @resource.expects(:warning) - - @harness.schedule(@resource).should be_nil - end - - it "should return nil if the resource specifies no schedule" do - @harness.schedule(@resource).should be_nil - end - - it "should fail if the named schedule cannot be found" do - @resource[:schedule] = "whatever" - @resource.expects(:fail) - @harness.schedule(@resource) - end - - it "should return the named schedule if it exists" do - sched = Puppet::Type.type(:schedule).new(:name => "sched") - @catalog.add_resource(sched) - @resource[:schedule] = "sched" - @harness.schedule(@resource).to_s.should == sched.to_s - end - end - - describe "when determining if a resource is scheduled" do - before do - @catalog = Puppet::Resource::Catalog.new - @resource.catalog = @catalog - @status = Puppet::Resource::Status.new(@resource) - end - - it "should return true if 'ignoreschedules' is set" do - Puppet[:ignoreschedules] = true - @resource[:schedule] = "meh" - @harness.should be_scheduled(@status, @resource) - end - - it "should return true if the resource has no schedule set" do - @harness.should be_scheduled(@status, @resource) - end - - it "should return the result of matching the schedule with the cached 'checked' time if a schedule is set" do - t = Time.now - @harness.expects(:cached).with(@resource, :checked).returns(t) - - sched = Puppet::Type.type(:schedule).new(:name => "sched") - @catalog.add_resource(sched) - @resource[:schedule] = "sched" - - sched.expects(:match?).with(t.to_i).returns "feh" - - @harness.scheduled?(@status, @resource).should == "feh" - end - end - - it "should be able to cache data in the Storage module" do - data = {} - Puppet::Util::Storage.expects(:cache).with(@resource).returns data - @harness.cache(@resource, :foo, "something") - - data[:foo].should == "something" - end - - it "should be able to retrieve data from the cache" do - data = {:foo => "other"} - Puppet::Util::Storage.expects(:cache).with(@resource).returns data - @harness.cached(@resource, :foo).should == "other" - end -end diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb new file mode 100755 index 000000000..999dc77e1 --- /dev/null +++ b/spec/unit/transaction_spec.rb @@ -0,0 +1,452 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'puppet/transaction' + +def without_warnings + flag = $VERBOSE + $VERBOSE = nil + yield + $VERBOSE = flag +end + +describe Puppet::Transaction do + before do + @basepath = Puppet.features.posix? ? "/what/ever" : "C:/tmp" + @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) + end + + it "should delegate its event list to the event manager" do + @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) + @transaction.event_manager.expects(:events).returns %w{my events} + @transaction.events.should == %w{my events} + end + + it "should delegate adding times to its report" do + @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) + @transaction.report.expects(:add_times).with(:foo, 10) + @transaction.report.expects(:add_times).with(:bar, 20) + + @transaction.add_times :foo => 10, :bar => 20 + end + + it "should be able to accept resource status instances" do + resource = Puppet::Type.type(:notify).new :title => "foobar" + status = Puppet::Resource::Status.new(resource) + @transaction.add_resource_status(status) + @transaction.resource_status(resource).should equal(status) + end + + it "should be able to look resource status up by resource reference" do + resource = Puppet::Type.type(:notify).new :title => "foobar" + status = Puppet::Resource::Status.new(resource) + @transaction.add_resource_status(status) + @transaction.resource_status(resource.to_s).should equal(status) + end + + # This will basically only ever be used during testing. + it "should automatically create resource statuses if asked for a non-existent status" do + resource = Puppet::Type.type(:notify).new :title => "foobar" + @transaction.resource_status(resource).should be_instance_of(Puppet::Resource::Status) + end + + it "should add provided resource statuses to its report" do + resource = Puppet::Type.type(:notify).new :title => "foobar" + status = Puppet::Resource::Status.new(resource) + @transaction.add_resource_status(status) + @transaction.report.resource_statuses[resource.to_s].should equal(status) + end + + it "should calculate metrics on and report the report when asked to generate a report" do + @transaction.report.expects(:calculate_metrics) + @transaction.generate_report.should equal(@transaction.report) + end + + it "should consider a resource to be failed if a status instance exists for that resource and indicates it is failed" do + resource = Puppet::Type.type(:notify).new :name => "yayness" + status = Puppet::Resource::Status.new(resource) + status.failed = "some message" + @transaction.add_resource_status(status) + @transaction.should be_failed(resource) + end + + it "should not consider a resource to be failed if a status instance exists for that resource but indicates it is not failed" do + resource = Puppet::Type.type(:notify).new :name => "yayness" + status = Puppet::Resource::Status.new(resource) + @transaction.add_resource_status(status) + @transaction.should_not be_failed(resource) + end + + it "should consider there to be failed resources if any statuses are marked failed" do + resource = Puppet::Type.type(:notify).new :name => "yayness" + status = Puppet::Resource::Status.new(resource) + status.failed = "some message" + @transaction.add_resource_status(status) + @transaction.should be_any_failed + end + + it "should not consider there to be failed resources if no statuses are marked failed" do + resource = Puppet::Type.type(:notify).new :name => "yayness" + status = Puppet::Resource::Status.new(resource) + @transaction.add_resource_status(status) + @transaction.should_not be_any_failed + end + + it "should be possible to replace the report object" do + report = Puppet::Transaction::Report.new + @transaction.report = report + + @transaction.report.should == report + end + + it "should consider a resource to have failed dependencies if any of its dependencies are failed" + + describe "when initializing" do + it "should create an event manager" do + @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) + @transaction.event_manager.should be_instance_of(Puppet::Transaction::EventManager) + @transaction.event_manager.transaction.should equal(@transaction) + end + + it "should create a resource harness" do + @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) + @transaction.resource_harness.should be_instance_of(Puppet::Transaction::ResourceHarness) + @transaction.resource_harness.transaction.should equal(@transaction) + end + end + + describe "when evaluating a resource" do + before do + @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) + @transaction.stubs(:eval_children_and_apply_resource) + @transaction.stubs(:skip?).returns false + + @resource = Puppet::Type.type(:file).new :path => @basepath + end + + it "should check whether the resource should be skipped" do + @transaction.expects(:skip?).with(@resource).returns false + + @transaction.eval_resource(@resource) + end + + it "should eval and apply children" do + @transaction.expects(:eval_children_and_apply_resource).with(@resource, nil) + + @transaction.eval_resource(@resource) + end + + it "should process events" do + @transaction.event_manager.expects(:process_events).with(@resource) + + @transaction.eval_resource(@resource) + end + + describe "and the resource should be skipped" do + before do + @transaction.expects(:skip?).with(@resource).returns true + end + + it "should mark the resource's status as skipped" do + @transaction.eval_resource(@resource) + @transaction.resource_status(@resource).should be_skipped + end + end + end + + describe "when applying a resource" do + before do + @resource = Puppet::Type.type(:file).new :path => @basepath + @status = Puppet::Resource::Status.new(@resource) + + @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) + @transaction.event_manager.stubs(:queue_events) + @transaction.resource_harness.stubs(:evaluate).returns(@status) + end + + it "should use its resource harness to apply the resource" do + @transaction.resource_harness.expects(:evaluate).with(@resource) + @transaction.apply(@resource) + end + + it "should add the resulting resource status to its status list" do + @transaction.apply(@resource) + @transaction.resource_status(@resource).should be_instance_of(Puppet::Resource::Status) + end + + it "should queue any events added to the resource status" do + @status.expects(:events).returns %w{a b} + @transaction.event_manager.expects(:queue_events).with(@resource, ["a", "b"]) + @transaction.apply(@resource) + end + + it "should log and skip any resources that cannot be applied" do + @transaction.resource_harness.expects(:evaluate).raises ArgumentError + @resource.expects(:err) + @transaction.apply(@resource) + @transaction.report.resource_statuses[@resource.to_s].should be_nil + end + end + + describe "when generating resources" do + it "should call 'generate' on all created resources" do + first = Puppet::Type.type(:notify).new(:name => "first") + second = Puppet::Type.type(:notify).new(:name => "second") + third = Puppet::Type.type(:notify).new(:name => "third") + + @catalog = Puppet::Resource::Catalog.new + @transaction = Puppet::Transaction.new(@catalog) + + first.expects(:generate).returns [second] + second.expects(:generate).returns [third] + third.expects(:generate) + + @transaction.generate_additional_resources(first, :generate) + end + + it "should finish all resources" do + generator = stub 'generator', :depthfirst? => true, :tags => [] + resource = stub 'resource', :tag => nil + + @catalog = Puppet::Resource::Catalog.new + @transaction = Puppet::Transaction.new(@catalog) + + generator.expects(:generate).returns [resource] + + @catalog.expects(:add_resource).yields(resource) + + resource.expects(:finish) + + @transaction.generate_additional_resources(generator, :generate) + end + + it "should skip generated resources that conflict with existing resources" do + generator = mock 'generator', :tags => [] + resource = stub 'resource', :tag => nil + + @catalog = Puppet::Resource::Catalog.new + @transaction = Puppet::Transaction.new(@catalog) + + generator.expects(:generate).returns [resource] + + @catalog.expects(:add_resource).raises(Puppet::Resource::Catalog::DuplicateResourceError.new("foo")) + + resource.expects(:finish).never + resource.expects(:info) # log that it's skipped + + @transaction.generate_additional_resources(generator, :generate).should be_empty + end + + it "should copy all tags to the newly generated resources" do + child = stub 'child' + generator = stub 'resource', :tags => ["one", "two"] + + @catalog = Puppet::Resource::Catalog.new + @transaction = Puppet::Transaction.new(@catalog) + + generator.stubs(:generate).returns [child] + @catalog.stubs(:add_resource) + + child.expects(:tag).with("one", "two") + + @transaction.generate_additional_resources(generator, :generate) + end + end + + describe "when skipping a resource" do + before :each do + @resource = Puppet::Type.type(:notify).new :name => "foo" + @catalog = Puppet::Resource::Catalog.new + @resource.catalog = @catalog + @transaction = Puppet::Transaction.new(@catalog) + end + + it "should skip resource with missing tags" do + @transaction.stubs(:missing_tags?).returns(true) + @transaction.should be_skip(@resource) + end + + it "should skip unscheduled resources" do + @transaction.stubs(:scheduled?).returns(false) + @transaction.should be_skip(@resource) + end + + it "should skip resources with failed dependencies" do + @transaction.stubs(:failed_dependencies?).returns(true) + @transaction.should be_skip(@resource) + end + + it "should skip virtual resource" do + @resource.stubs(:virtual?).returns true + @transaction.should be_skip(@resource) + end + end + + describe "when determining if tags are missing" do + before :each do + @resource = Puppet::Type.type(:notify).new :name => "foo" + @catalog = Puppet::Resource::Catalog.new + @resource.catalog = @catalog + @transaction = Puppet::Transaction.new(@catalog) + + @transaction.stubs(:ignore_tags?).returns false + end + + it "should not be missing tags if tags are being ignored" do + @transaction.expects(:ignore_tags?).returns true + + @resource.expects(:tagged?).never + + @transaction.should_not be_missing_tags(@resource) + end + + it "should not be missing tags if the transaction tags are empty" do + @transaction.tags = [] + @resource.expects(:tagged?).never + @transaction.should_not be_missing_tags(@resource) + end + + it "should otherwise let the resource determine if it is missing tags" do + tags = ['one', 'two'] + @transaction.tags = tags + @resource.expects(:tagged?).with(*tags).returns(false) + @transaction.should be_missing_tags(@resource) + end + end + + describe "when determining if a resource should be scheduled" do + before :each do + @resource = Puppet::Type.type(:notify).new :name => "foo" + @catalog = Puppet::Resource::Catalog.new + @resource.catalog = @catalog + @transaction = Puppet::Transaction.new(@catalog) + end + + it "should always schedule resources if 'ignoreschedules' is set" do + @transaction.ignoreschedules = true + @transaction.resource_harness.expects(:scheduled?).never + + @transaction.should be_scheduled(@resource) + end + + it "should let the resource harness determine whether the resource should be scheduled" do + @transaction.resource_harness.expects(:scheduled?).with(@transaction.resource_status(@resource), @resource).returns "feh" + + @transaction.scheduled?(@resource).should == "feh" + end + end + + describe "when prefetching" do + it "should match resources by name, not title" do + @catalog = Puppet::Resource::Catalog.new + @transaction = Puppet::Transaction.new(@catalog) + + # Have both a title and name + resource = Puppet::Type.type(:sshkey).create :title => "foo", :name => "bar", :type => :dsa, :key => "eh" + @catalog.add_resource resource + + resource.provider.class.expects(:prefetch).with("bar" => resource) + + @transaction.prefetch + end + end + + it "should return all resources for which the resource status indicates the resource has changed when determinig changed resources" do + @catalog = Puppet::Resource::Catalog.new + @transaction = Puppet::Transaction.new(@catalog) + names = [] + 2.times do |i| + name = File.join(@basepath, "file#{i}") + resource = Puppet::Type.type(:file).new :path => name + names << resource.to_s + @catalog.add_resource resource + @transaction.add_resource_status Puppet::Resource::Status.new(resource) + end + + @transaction.resource_status(names[0]).changed = true + + @transaction.changed?.should == [@catalog.resource(names[0])] + end + + describe 'when checking application run state' do + before do + without_warnings { Puppet::Application = Class.new(Puppet::Application) } + @catalog = Puppet::Resource::Catalog.new + @transaction = Puppet::Transaction.new(@catalog) + end + + after do + without_warnings { Puppet::Application = Puppet::Application.superclass } + end + + it 'should return true for :stop_processing? if Puppet::Application.stop_requested? is true' do + Puppet::Application.stubs(:stop_requested?).returns(true) + @transaction.stop_processing?.should be_true + end + + it 'should return false for :stop_processing? if Puppet::Application.stop_requested? is false' do + Puppet::Application.stubs(:stop_requested?).returns(false) + @transaction.stop_processing?.should be_false + end + + describe 'within an evaluate call' do + before do + @resource = stub 'resource', :ref => 'some_ref' + @catalog.add_resource @resource + @transaction.stubs(:prepare) + @transaction.sorted_resources = [@resource] + end + + it 'should stop processing if :stop_processing? is true' do + @transaction.expects(:stop_processing?).returns(true) + @transaction.expects(:eval_resource).never + @transaction.evaluate + end + + it 'should continue processing if :stop_processing? is false' do + @transaction.expects(:stop_processing?).returns(false) + @transaction.expects(:eval_resource).returns(nil) + @transaction.evaluate + end + end + end +end + +describe Puppet::Transaction, " when determining tags" do + before do + @config = Puppet::Resource::Catalog.new + @transaction = Puppet::Transaction.new(@config) + end + + it "should default to the tags specified in the :tags setting" do + Puppet.expects(:[]).with(:tags).returns("one") + @transaction.tags.should == %w{one} + end + + it "should split tags based on ','" do + Puppet.expects(:[]).with(:tags).returns("one,two") + @transaction.tags.should == %w{one two} + end + + it "should use any tags set after creation" do + Puppet.expects(:[]).with(:tags).never + @transaction.tags = %w{one two} + @transaction.tags.should == %w{one two} + end + + it "should always convert assigned tags to an array" do + @transaction.tags = "one::two" + @transaction.tags.should == %w{one::two} + end + + it "should accept a comma-delimited string" do + @transaction.tags = "one, two" + @transaction.tags.should == %w{one two} + end + + it "should accept an empty string" do + @transaction.tags = "" + @transaction.tags.should == [] + end +end diff --git a/spec/unit/transaction_spec_spec.rb b/spec/unit/transaction_spec_spec.rb deleted file mode 100755 index 999dc77e1..000000000 --- a/spec/unit/transaction_spec_spec.rb +++ /dev/null @@ -1,452 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -require 'puppet/transaction' - -def without_warnings - flag = $VERBOSE - $VERBOSE = nil - yield - $VERBOSE = flag -end - -describe Puppet::Transaction do - before do - @basepath = Puppet.features.posix? ? "/what/ever" : "C:/tmp" - @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) - end - - it "should delegate its event list to the event manager" do - @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) - @transaction.event_manager.expects(:events).returns %w{my events} - @transaction.events.should == %w{my events} - end - - it "should delegate adding times to its report" do - @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) - @transaction.report.expects(:add_times).with(:foo, 10) - @transaction.report.expects(:add_times).with(:bar, 20) - - @transaction.add_times :foo => 10, :bar => 20 - end - - it "should be able to accept resource status instances" do - resource = Puppet::Type.type(:notify).new :title => "foobar" - status = Puppet::Resource::Status.new(resource) - @transaction.add_resource_status(status) - @transaction.resource_status(resource).should equal(status) - end - - it "should be able to look resource status up by resource reference" do - resource = Puppet::Type.type(:notify).new :title => "foobar" - status = Puppet::Resource::Status.new(resource) - @transaction.add_resource_status(status) - @transaction.resource_status(resource.to_s).should equal(status) - end - - # This will basically only ever be used during testing. - it "should automatically create resource statuses if asked for a non-existent status" do - resource = Puppet::Type.type(:notify).new :title => "foobar" - @transaction.resource_status(resource).should be_instance_of(Puppet::Resource::Status) - end - - it "should add provided resource statuses to its report" do - resource = Puppet::Type.type(:notify).new :title => "foobar" - status = Puppet::Resource::Status.new(resource) - @transaction.add_resource_status(status) - @transaction.report.resource_statuses[resource.to_s].should equal(status) - end - - it "should calculate metrics on and report the report when asked to generate a report" do - @transaction.report.expects(:calculate_metrics) - @transaction.generate_report.should equal(@transaction.report) - end - - it "should consider a resource to be failed if a status instance exists for that resource and indicates it is failed" do - resource = Puppet::Type.type(:notify).new :name => "yayness" - status = Puppet::Resource::Status.new(resource) - status.failed = "some message" - @transaction.add_resource_status(status) - @transaction.should be_failed(resource) - end - - it "should not consider a resource to be failed if a status instance exists for that resource but indicates it is not failed" do - resource = Puppet::Type.type(:notify).new :name => "yayness" - status = Puppet::Resource::Status.new(resource) - @transaction.add_resource_status(status) - @transaction.should_not be_failed(resource) - end - - it "should consider there to be failed resources if any statuses are marked failed" do - resource = Puppet::Type.type(:notify).new :name => "yayness" - status = Puppet::Resource::Status.new(resource) - status.failed = "some message" - @transaction.add_resource_status(status) - @transaction.should be_any_failed - end - - it "should not consider there to be failed resources if no statuses are marked failed" do - resource = Puppet::Type.type(:notify).new :name => "yayness" - status = Puppet::Resource::Status.new(resource) - @transaction.add_resource_status(status) - @transaction.should_not be_any_failed - end - - it "should be possible to replace the report object" do - report = Puppet::Transaction::Report.new - @transaction.report = report - - @transaction.report.should == report - end - - it "should consider a resource to have failed dependencies if any of its dependencies are failed" - - describe "when initializing" do - it "should create an event manager" do - @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) - @transaction.event_manager.should be_instance_of(Puppet::Transaction::EventManager) - @transaction.event_manager.transaction.should equal(@transaction) - end - - it "should create a resource harness" do - @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) - @transaction.resource_harness.should be_instance_of(Puppet::Transaction::ResourceHarness) - @transaction.resource_harness.transaction.should equal(@transaction) - end - end - - describe "when evaluating a resource" do - before do - @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) - @transaction.stubs(:eval_children_and_apply_resource) - @transaction.stubs(:skip?).returns false - - @resource = Puppet::Type.type(:file).new :path => @basepath - end - - it "should check whether the resource should be skipped" do - @transaction.expects(:skip?).with(@resource).returns false - - @transaction.eval_resource(@resource) - end - - it "should eval and apply children" do - @transaction.expects(:eval_children_and_apply_resource).with(@resource, nil) - - @transaction.eval_resource(@resource) - end - - it "should process events" do - @transaction.event_manager.expects(:process_events).with(@resource) - - @transaction.eval_resource(@resource) - end - - describe "and the resource should be skipped" do - before do - @transaction.expects(:skip?).with(@resource).returns true - end - - it "should mark the resource's status as skipped" do - @transaction.eval_resource(@resource) - @transaction.resource_status(@resource).should be_skipped - end - end - end - - describe "when applying a resource" do - before do - @resource = Puppet::Type.type(:file).new :path => @basepath - @status = Puppet::Resource::Status.new(@resource) - - @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) - @transaction.event_manager.stubs(:queue_events) - @transaction.resource_harness.stubs(:evaluate).returns(@status) - end - - it "should use its resource harness to apply the resource" do - @transaction.resource_harness.expects(:evaluate).with(@resource) - @transaction.apply(@resource) - end - - it "should add the resulting resource status to its status list" do - @transaction.apply(@resource) - @transaction.resource_status(@resource).should be_instance_of(Puppet::Resource::Status) - end - - it "should queue any events added to the resource status" do - @status.expects(:events).returns %w{a b} - @transaction.event_manager.expects(:queue_events).with(@resource, ["a", "b"]) - @transaction.apply(@resource) - end - - it "should log and skip any resources that cannot be applied" do - @transaction.resource_harness.expects(:evaluate).raises ArgumentError - @resource.expects(:err) - @transaction.apply(@resource) - @transaction.report.resource_statuses[@resource.to_s].should be_nil - end - end - - describe "when generating resources" do - it "should call 'generate' on all created resources" do - first = Puppet::Type.type(:notify).new(:name => "first") - second = Puppet::Type.type(:notify).new(:name => "second") - third = Puppet::Type.type(:notify).new(:name => "third") - - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) - - first.expects(:generate).returns [second] - second.expects(:generate).returns [third] - third.expects(:generate) - - @transaction.generate_additional_resources(first, :generate) - end - - it "should finish all resources" do - generator = stub 'generator', :depthfirst? => true, :tags => [] - resource = stub 'resource', :tag => nil - - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) - - generator.expects(:generate).returns [resource] - - @catalog.expects(:add_resource).yields(resource) - - resource.expects(:finish) - - @transaction.generate_additional_resources(generator, :generate) - end - - it "should skip generated resources that conflict with existing resources" do - generator = mock 'generator', :tags => [] - resource = stub 'resource', :tag => nil - - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) - - generator.expects(:generate).returns [resource] - - @catalog.expects(:add_resource).raises(Puppet::Resource::Catalog::DuplicateResourceError.new("foo")) - - resource.expects(:finish).never - resource.expects(:info) # log that it's skipped - - @transaction.generate_additional_resources(generator, :generate).should be_empty - end - - it "should copy all tags to the newly generated resources" do - child = stub 'child' - generator = stub 'resource', :tags => ["one", "two"] - - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) - - generator.stubs(:generate).returns [child] - @catalog.stubs(:add_resource) - - child.expects(:tag).with("one", "two") - - @transaction.generate_additional_resources(generator, :generate) - end - end - - describe "when skipping a resource" do - before :each do - @resource = Puppet::Type.type(:notify).new :name => "foo" - @catalog = Puppet::Resource::Catalog.new - @resource.catalog = @catalog - @transaction = Puppet::Transaction.new(@catalog) - end - - it "should skip resource with missing tags" do - @transaction.stubs(:missing_tags?).returns(true) - @transaction.should be_skip(@resource) - end - - it "should skip unscheduled resources" do - @transaction.stubs(:scheduled?).returns(false) - @transaction.should be_skip(@resource) - end - - it "should skip resources with failed dependencies" do - @transaction.stubs(:failed_dependencies?).returns(true) - @transaction.should be_skip(@resource) - end - - it "should skip virtual resource" do - @resource.stubs(:virtual?).returns true - @transaction.should be_skip(@resource) - end - end - - describe "when determining if tags are missing" do - before :each do - @resource = Puppet::Type.type(:notify).new :name => "foo" - @catalog = Puppet::Resource::Catalog.new - @resource.catalog = @catalog - @transaction = Puppet::Transaction.new(@catalog) - - @transaction.stubs(:ignore_tags?).returns false - end - - it "should not be missing tags if tags are being ignored" do - @transaction.expects(:ignore_tags?).returns true - - @resource.expects(:tagged?).never - - @transaction.should_not be_missing_tags(@resource) - end - - it "should not be missing tags if the transaction tags are empty" do - @transaction.tags = [] - @resource.expects(:tagged?).never - @transaction.should_not be_missing_tags(@resource) - end - - it "should otherwise let the resource determine if it is missing tags" do - tags = ['one', 'two'] - @transaction.tags = tags - @resource.expects(:tagged?).with(*tags).returns(false) - @transaction.should be_missing_tags(@resource) - end - end - - describe "when determining if a resource should be scheduled" do - before :each do - @resource = Puppet::Type.type(:notify).new :name => "foo" - @catalog = Puppet::Resource::Catalog.new - @resource.catalog = @catalog - @transaction = Puppet::Transaction.new(@catalog) - end - - it "should always schedule resources if 'ignoreschedules' is set" do - @transaction.ignoreschedules = true - @transaction.resource_harness.expects(:scheduled?).never - - @transaction.should be_scheduled(@resource) - end - - it "should let the resource harness determine whether the resource should be scheduled" do - @transaction.resource_harness.expects(:scheduled?).with(@transaction.resource_status(@resource), @resource).returns "feh" - - @transaction.scheduled?(@resource).should == "feh" - end - end - - describe "when prefetching" do - it "should match resources by name, not title" do - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) - - # Have both a title and name - resource = Puppet::Type.type(:sshkey).create :title => "foo", :name => "bar", :type => :dsa, :key => "eh" - @catalog.add_resource resource - - resource.provider.class.expects(:prefetch).with("bar" => resource) - - @transaction.prefetch - end - end - - it "should return all resources for which the resource status indicates the resource has changed when determinig changed resources" do - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) - names = [] - 2.times do |i| - name = File.join(@basepath, "file#{i}") - resource = Puppet::Type.type(:file).new :path => name - names << resource.to_s - @catalog.add_resource resource - @transaction.add_resource_status Puppet::Resource::Status.new(resource) - end - - @transaction.resource_status(names[0]).changed = true - - @transaction.changed?.should == [@catalog.resource(names[0])] - end - - describe 'when checking application run state' do - before do - without_warnings { Puppet::Application = Class.new(Puppet::Application) } - @catalog = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@catalog) - end - - after do - without_warnings { Puppet::Application = Puppet::Application.superclass } - end - - it 'should return true for :stop_processing? if Puppet::Application.stop_requested? is true' do - Puppet::Application.stubs(:stop_requested?).returns(true) - @transaction.stop_processing?.should be_true - end - - it 'should return false for :stop_processing? if Puppet::Application.stop_requested? is false' do - Puppet::Application.stubs(:stop_requested?).returns(false) - @transaction.stop_processing?.should be_false - end - - describe 'within an evaluate call' do - before do - @resource = stub 'resource', :ref => 'some_ref' - @catalog.add_resource @resource - @transaction.stubs(:prepare) - @transaction.sorted_resources = [@resource] - end - - it 'should stop processing if :stop_processing? is true' do - @transaction.expects(:stop_processing?).returns(true) - @transaction.expects(:eval_resource).never - @transaction.evaluate - end - - it 'should continue processing if :stop_processing? is false' do - @transaction.expects(:stop_processing?).returns(false) - @transaction.expects(:eval_resource).returns(nil) - @transaction.evaluate - end - end - end -end - -describe Puppet::Transaction, " when determining tags" do - before do - @config = Puppet::Resource::Catalog.new - @transaction = Puppet::Transaction.new(@config) - end - - it "should default to the tags specified in the :tags setting" do - Puppet.expects(:[]).with(:tags).returns("one") - @transaction.tags.should == %w{one} - end - - it "should split tags based on ','" do - Puppet.expects(:[]).with(:tags).returns("one,two") - @transaction.tags.should == %w{one two} - end - - it "should use any tags set after creation" do - Puppet.expects(:[]).with(:tags).never - @transaction.tags = %w{one two} - @transaction.tags.should == %w{one two} - end - - it "should always convert assigned tags to an array" do - @transaction.tags = "one::two" - @transaction.tags.should == %w{one::two} - end - - it "should accept a comma-delimited string" do - @transaction.tags = "one, two" - @transaction.tags.should == %w{one two} - end - - it "should accept an empty string" do - @transaction.tags = "" - @transaction.tags.should == [] - end -end diff --git a/spec/unit/transportable_spec.rb b/spec/unit/transportable_spec.rb new file mode 100644 index 000000000..e69de29bb diff --git a/spec/unit/transportable_spec_spec.rb b/spec/unit/transportable_spec_spec.rb deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/unit/type/augeas_spec.rb b/spec/unit/type/augeas_spec.rb new file mode 100644 index 000000000..b71f55f26 --- /dev/null +++ b/spec/unit/type/augeas_spec.rb @@ -0,0 +1,122 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +augeas = Puppet::Type.type(:augeas) + +describe augeas do + describe "when augeas is present" do + confine "Augeas is unavailable" => Puppet.features.augeas? + + it "should have a default provider inheriting from Puppet::Provider" do + augeas.defaultprovider.ancestors.should be_include(Puppet::Provider) + end + + it "should have a valid provider" do + augeas.new(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) + end + end + + describe "basic structure" do + it "should be able to create a instance" do + provider_class = Puppet::Type::Augeas.provider(Puppet::Type::Augeas.providers[0]) + Puppet::Type::Augeas.expects(:defaultprovider).returns provider_class + augeas.new(:name => "bar").should_not be_nil + end + + it "should have an parse_commands feature" do + augeas.provider_feature(:parse_commands).should_not be_nil + end + + it "should have an need_to_run? feature" do + augeas.provider_feature(:need_to_run?).should_not be_nil + end + + it "should have an execute_changes feature" do + augeas.provider_feature(:execute_changes).should_not be_nil + end + + properties = [:returns] + params = [:name, :context, :onlyif, :changes, :root, :load_path, :type_check] + + properties.each do |property| + it "should have a %s property" % property do + augeas.attrclass(property).ancestors.should be_include(Puppet::Property) + end + + it "should have documentation for its %s property" % property do + augeas.attrclass(property).doc.should be_instance_of(String) + end + end + + params.each do |param| + it "should have a %s parameter" % param do + augeas.attrclass(param).ancestors.should be_include(Puppet::Parameter) + end + + it "should have documentation for its %s parameter" % param do + augeas.attrclass(param).doc.should be_instance_of(String) + end + end + end + + describe "default values" do + before do + provider_class = augeas.provider(augeas.providers[0]) + augeas.expects(:defaultprovider).returns provider_class + end + + it "should be blank for context" do + augeas.new(:name => :context)[:context].should == "" + end + + it "should be blank for onlyif" do + augeas.new(:name => :onlyif)[:onlyif].should == "" + end + + it "should be blank for load_path" do + augeas.new(:name => :load_path)[:load_path].should == "" + end + + it "should be / for root" do + augeas.new(:name => :root)[:root].should == "/" + end + + it "should be false for type_check" do + augeas.new(:name => :type_check)[:type_check].should == :false + end + end + + describe "provider interaction" do + + it "should return 0 if it does not need to run" do + provider = stub("provider", :need_to_run? => false) + resource = stub('resource', :resource => nil, :provider => provider, :line => nil, :file => nil) + changes = augeas.attrclass(:returns).new(:resource => resource) + changes.retrieve.should == 0 + end + + it "should return :need_to_run if it needs to run" do + provider = stub("provider", :need_to_run? => true) + resource = stub('resource', :resource => nil, :provider => provider, :line => nil, :file => nil) + changes = augeas.attrclass(:returns).new(:resource => resource) + changes.retrieve.should == :need_to_run + end + end + + describe "loading specific files" do + it "should require lens when incl is used" do + lambda { augeas.new(:name => :no_lens, :incl => "/etc/hosts")}.should raise_error(Puppet::Error) + end + + it "should require incl when lens is used" do + lambda { augeas.new(:name => :no_incl, :lens => "Hosts.lns") }.should raise_error(Puppet::Error) + end + + it "should set the context when a specific file is used" do + fake_provider = stub_everything "fake_provider" + augeas.stubs(:defaultprovider).returns fake_provider + augeas.new(:name => :no_incl, :lens => "Hosts.lns", :incl => "/etc/hosts")[:context].should == "/files/etc/hosts" + end + end +end diff --git a/spec/unit/type/augeas_spec_spec.rb b/spec/unit/type/augeas_spec_spec.rb deleted file mode 100644 index b71f55f26..000000000 --- a/spec/unit/type/augeas_spec_spec.rb +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -augeas = Puppet::Type.type(:augeas) - -describe augeas do - describe "when augeas is present" do - confine "Augeas is unavailable" => Puppet.features.augeas? - - it "should have a default provider inheriting from Puppet::Provider" do - augeas.defaultprovider.ancestors.should be_include(Puppet::Provider) - end - - it "should have a valid provider" do - augeas.new(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) - end - end - - describe "basic structure" do - it "should be able to create a instance" do - provider_class = Puppet::Type::Augeas.provider(Puppet::Type::Augeas.providers[0]) - Puppet::Type::Augeas.expects(:defaultprovider).returns provider_class - augeas.new(:name => "bar").should_not be_nil - end - - it "should have an parse_commands feature" do - augeas.provider_feature(:parse_commands).should_not be_nil - end - - it "should have an need_to_run? feature" do - augeas.provider_feature(:need_to_run?).should_not be_nil - end - - it "should have an execute_changes feature" do - augeas.provider_feature(:execute_changes).should_not be_nil - end - - properties = [:returns] - params = [:name, :context, :onlyif, :changes, :root, :load_path, :type_check] - - properties.each do |property| - it "should have a %s property" % property do - augeas.attrclass(property).ancestors.should be_include(Puppet::Property) - end - - it "should have documentation for its %s property" % property do - augeas.attrclass(property).doc.should be_instance_of(String) - end - end - - params.each do |param| - it "should have a %s parameter" % param do - augeas.attrclass(param).ancestors.should be_include(Puppet::Parameter) - end - - it "should have documentation for its %s parameter" % param do - augeas.attrclass(param).doc.should be_instance_of(String) - end - end - end - - describe "default values" do - before do - provider_class = augeas.provider(augeas.providers[0]) - augeas.expects(:defaultprovider).returns provider_class - end - - it "should be blank for context" do - augeas.new(:name => :context)[:context].should == "" - end - - it "should be blank for onlyif" do - augeas.new(:name => :onlyif)[:onlyif].should == "" - end - - it "should be blank for load_path" do - augeas.new(:name => :load_path)[:load_path].should == "" - end - - it "should be / for root" do - augeas.new(:name => :root)[:root].should == "/" - end - - it "should be false for type_check" do - augeas.new(:name => :type_check)[:type_check].should == :false - end - end - - describe "provider interaction" do - - it "should return 0 if it does not need to run" do - provider = stub("provider", :need_to_run? => false) - resource = stub('resource', :resource => nil, :provider => provider, :line => nil, :file => nil) - changes = augeas.attrclass(:returns).new(:resource => resource) - changes.retrieve.should == 0 - end - - it "should return :need_to_run if it needs to run" do - provider = stub("provider", :need_to_run? => true) - resource = stub('resource', :resource => nil, :provider => provider, :line => nil, :file => nil) - changes = augeas.attrclass(:returns).new(:resource => resource) - changes.retrieve.should == :need_to_run - end - end - - describe "loading specific files" do - it "should require lens when incl is used" do - lambda { augeas.new(:name => :no_lens, :incl => "/etc/hosts")}.should raise_error(Puppet::Error) - end - - it "should require incl when lens is used" do - lambda { augeas.new(:name => :no_incl, :lens => "Hosts.lns") }.should raise_error(Puppet::Error) - end - - it "should set the context when a specific file is used" do - fake_provider = stub_everything "fake_provider" - augeas.stubs(:defaultprovider).returns fake_provider - augeas.new(:name => :no_incl, :lens => "Hosts.lns", :incl => "/etc/hosts")[:context].should == "/files/etc/hosts" - end - end -end diff --git a/spec/unit/type/component_spec.rb b/spec/unit/type/component_spec.rb new file mode 100755 index 000000000..ab60e02f2 --- /dev/null +++ b/spec/unit/type/component_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +component = Puppet::Type.type(:component) + +describe component do + it "should have a :name attribute" do + component.attrclass(:name).should_not be_nil + end + + it "should use Class as its type when a normal string is provided as the title" do + component.new(:name => "bar").ref.should == "Class[Bar]" + end + + it "should always produce a resource reference string as its title" do + component.new(:name => "bar").title.should == "Class[Bar]" + end + + it "should have a reference string equivalent to its title" do + comp = component.new(:name => "Foo[bar]") + comp.title.should == comp.ref + end + + it "should alias itself to its reference if it has a catalog and the catalog does not already have a resource with the same reference" do + catalog = mock 'catalog' + catalog.expects(:resource).with("Foo[bar]").returns nil + + catalog.expects(:alias).with { |resource, name| resource.is_a?(component) and name == "Foo[bar]" } + + component.new(:name => "Foo[bar]", :catalog => catalog) + end + + it "should not fail when provided an invalid value" do + comp = component.new(:name => "Foo[bar]") + lambda { comp[:yayness] = "ey" }.should_not raise_error + end + + it "should return previously provided invalid values" do + comp = component.new(:name => "Foo[bar]") + comp[:yayness] = "eh" + comp[:yayness].should == "eh" + end + + it "should correctly support metaparameters" do + comp = component.new(:name => "Foo[bar]", :require => "Foo[bar]") + comp.parameter(:require).should be_instance_of(component.attrclass(:require)) + end + + describe "when building up the path" do + it "should produce the class name if the component models a class" do + component.new(:name => "Class[foo]").pathbuilder.must == ["Foo"] + end + + it "should produce an empty string if the component models the 'main' class" do + component.new(:name => "Class[main]").pathbuilder.must == [""] + end + + it "should produce a resource reference if the component does not model a class" do + component.new(:name => "Foo[bar]").pathbuilder.must == ["Foo[bar]"] + end + end +end diff --git a/spec/unit/type/component_spec_spec.rb b/spec/unit/type/component_spec_spec.rb deleted file mode 100755 index ab60e02f2..000000000 --- a/spec/unit/type/component_spec_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -component = Puppet::Type.type(:component) - -describe component do - it "should have a :name attribute" do - component.attrclass(:name).should_not be_nil - end - - it "should use Class as its type when a normal string is provided as the title" do - component.new(:name => "bar").ref.should == "Class[Bar]" - end - - it "should always produce a resource reference string as its title" do - component.new(:name => "bar").title.should == "Class[Bar]" - end - - it "should have a reference string equivalent to its title" do - comp = component.new(:name => "Foo[bar]") - comp.title.should == comp.ref - end - - it "should alias itself to its reference if it has a catalog and the catalog does not already have a resource with the same reference" do - catalog = mock 'catalog' - catalog.expects(:resource).with("Foo[bar]").returns nil - - catalog.expects(:alias).with { |resource, name| resource.is_a?(component) and name == "Foo[bar]" } - - component.new(:name => "Foo[bar]", :catalog => catalog) - end - - it "should not fail when provided an invalid value" do - comp = component.new(:name => "Foo[bar]") - lambda { comp[:yayness] = "ey" }.should_not raise_error - end - - it "should return previously provided invalid values" do - comp = component.new(:name => "Foo[bar]") - comp[:yayness] = "eh" - comp[:yayness].should == "eh" - end - - it "should correctly support metaparameters" do - comp = component.new(:name => "Foo[bar]", :require => "Foo[bar]") - comp.parameter(:require).should be_instance_of(component.attrclass(:require)) - end - - describe "when building up the path" do - it "should produce the class name if the component models a class" do - component.new(:name => "Class[foo]").pathbuilder.must == ["Foo"] - end - - it "should produce an empty string if the component models the 'main' class" do - component.new(:name => "Class[main]").pathbuilder.must == [""] - end - - it "should produce a resource reference if the component does not model a class" do - component.new(:name => "Foo[bar]").pathbuilder.must == ["Foo[bar]"] - end - end -end diff --git a/spec/unit/type/computer_spec.rb b/spec/unit/type/computer_spec.rb new file mode 100755 index 000000000..c81ee45ee --- /dev/null +++ b/spec/unit/type/computer_spec.rb @@ -0,0 +1,78 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +computer = Puppet::Type.type(:computer) + +describe Puppet::Type.type(:computer), " when checking computer objects" do + before do + provider_class = Puppet::Type::Computer.provider(Puppet::Type::Computer.providers[0]) + Puppet::Type::Computer.expects(:defaultprovider).returns provider_class + @resource = Puppet::Type::Computer.new( + :name => "puppetcomputertest", + :en_address => "aa:bb:cc:dd:ee:ff", + :ip_address => "1.2.3.4") + @properties = {} + @ensure = Puppet::Type::Computer.attrclass(:ensure).new(:resource => @resource) + end + + it "should be able to create a instance" do + provider_class = Puppet::Type::Computer.provider(Puppet::Type::Computer.providers[0]) + Puppet::Type::Computer.expects(:defaultprovider).returns provider_class + computer.new(:name => "bar").should_not be_nil + end + + properties = [:en_address, :ip_address] + params = [:name] + + properties.each do |property| + it "should have a %s property" % property do + computer.attrclass(property).ancestors.should be_include(Puppet::Property) + end + + it "should have documentation for its %s property" % property do + computer.attrclass(property).doc.should be_instance_of(String) + end + + it "should accept :absent as a value" do + prop = computer.attrclass(property).new(:resource => @resource) + prop.should = :absent + prop.should.must == :absent + end + end + + params.each do |param| + it "should have a %s parameter" % param do + computer.attrclass(param).ancestors.should be_include(Puppet::Parameter) + end + + it "should have documentation for its %s parameter" % param do + computer.attrclass(param).doc.should be_instance_of(String) + end + end + + describe "default values" do + before do + provider_class = computer.provider(computer.providers[0]) + computer.expects(:defaultprovider).returns provider_class + end + + it "should be nil for en_address" do + computer.new(:name => :en_address)[:en_address].should == nil + end + + it "should be nil for ip_address" do + computer.new(:name => :ip_address)[:ip_address].should == nil + end + end + + describe "when managing the ensure property" do + it "should support a :present value" do + lambda { @ensure.should = :present }.should_not raise_error + end + + it "should support an :absent value" do + lambda { @ensure.should = :absent }.should_not raise_error + end + end +end diff --git a/spec/unit/type/computer_spec_spec.rb b/spec/unit/type/computer_spec_spec.rb deleted file mode 100755 index c81ee45ee..000000000 --- a/spec/unit/type/computer_spec_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -computer = Puppet::Type.type(:computer) - -describe Puppet::Type.type(:computer), " when checking computer objects" do - before do - provider_class = Puppet::Type::Computer.provider(Puppet::Type::Computer.providers[0]) - Puppet::Type::Computer.expects(:defaultprovider).returns provider_class - @resource = Puppet::Type::Computer.new( - :name => "puppetcomputertest", - :en_address => "aa:bb:cc:dd:ee:ff", - :ip_address => "1.2.3.4") - @properties = {} - @ensure = Puppet::Type::Computer.attrclass(:ensure).new(:resource => @resource) - end - - it "should be able to create a instance" do - provider_class = Puppet::Type::Computer.provider(Puppet::Type::Computer.providers[0]) - Puppet::Type::Computer.expects(:defaultprovider).returns provider_class - computer.new(:name => "bar").should_not be_nil - end - - properties = [:en_address, :ip_address] - params = [:name] - - properties.each do |property| - it "should have a %s property" % property do - computer.attrclass(property).ancestors.should be_include(Puppet::Property) - end - - it "should have documentation for its %s property" % property do - computer.attrclass(property).doc.should be_instance_of(String) - end - - it "should accept :absent as a value" do - prop = computer.attrclass(property).new(:resource => @resource) - prop.should = :absent - prop.should.must == :absent - end - end - - params.each do |param| - it "should have a %s parameter" % param do - computer.attrclass(param).ancestors.should be_include(Puppet::Parameter) - end - - it "should have documentation for its %s parameter" % param do - computer.attrclass(param).doc.should be_instance_of(String) - end - end - - describe "default values" do - before do - provider_class = computer.provider(computer.providers[0]) - computer.expects(:defaultprovider).returns provider_class - end - - it "should be nil for en_address" do - computer.new(:name => :en_address)[:en_address].should == nil - end - - it "should be nil for ip_address" do - computer.new(:name => :ip_address)[:ip_address].should == nil - end - end - - describe "when managing the ensure property" do - it "should support a :present value" do - lambda { @ensure.should = :present }.should_not raise_error - end - - it "should support an :absent value" do - lambda { @ensure.should = :absent }.should_not raise_error - end - end -end diff --git a/spec/unit/type/cron_spec.rb b/spec/unit/type/cron_spec.rb new file mode 100755 index 000000000..6951077e0 --- /dev/null +++ b/spec/unit/type/cron_spec.rb @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +describe Puppet::Type.type(:cron) do + before do + @cron = Puppet::Type.type(:cron).new( :name => "foo" ) + end + + it "it should accept an :environment that looks like a path" do + lambda do + @cron[:environment] = 'PATH=/bin:/usr/bin:/usr/sbin' + end.should_not raise_error + end + + it "should not accept environment variables that do not contain '='" do + lambda do + @cron[:environment] = "INVALID" + end.should raise_error(Puppet::Error) + end + + it "should accept empty environment variables that do not contain '='" do + lambda do + @cron[:environment] = "MAILTO=" + end.should_not raise_error(Puppet::Error) + end + + it "should accept 'absent'" do + lambda do + @cron[:environment] = 'absent' + end.should_not raise_error(Puppet::Error) + end +end diff --git a/spec/unit/type/cron_spec_spec.rb b/spec/unit/type/cron_spec_spec.rb deleted file mode 100755 index 6951077e0..000000000 --- a/spec/unit/type/cron_spec_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -describe Puppet::Type.type(:cron) do - before do - @cron = Puppet::Type.type(:cron).new( :name => "foo" ) - end - - it "it should accept an :environment that looks like a path" do - lambda do - @cron[:environment] = 'PATH=/bin:/usr/bin:/usr/sbin' - end.should_not raise_error - end - - it "should not accept environment variables that do not contain '='" do - lambda do - @cron[:environment] = "INVALID" - end.should raise_error(Puppet::Error) - end - - it "should accept empty environment variables that do not contain '='" do - lambda do - @cron[:environment] = "MAILTO=" - end.should_not raise_error(Puppet::Error) - end - - it "should accept 'absent'" do - lambda do - @cron[:environment] = 'absent' - end.should_not raise_error(Puppet::Error) - end -end diff --git a/spec/unit/type/exec_spec.rb b/spec/unit/type/exec_spec.rb new file mode 100755 index 000000000..1eaf80390 --- /dev/null +++ b/spec/unit/type/exec_spec.rb @@ -0,0 +1,162 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type.type(:exec) do + + def create_resource(command, output, exitstatus, returns = 0) + @user_name = 'some_user_name' + @group_name = 'some_group_name' + Puppet.features.stubs(:root?).returns(true) + @execer = Puppet::Type.type(:exec).new(:name => command, :path => @example_path, :user => @user_name, :group => @group_name, :returns => returns) + + status = stub "process" + status.stubs(:exitstatus).returns(exitstatus) + + Puppet::Util::SUIDManager.expects(:run_and_capture).with([command], @user_name, @group_name).returns([output, status]) + end + + def create_logging_resource(command, output, exitstatus, logoutput, loglevel, returns = 0) + create_resource(command, output, exitstatus, returns) + @execer[:logoutput] = logoutput + @execer[:loglevel] = loglevel + end + + def expect_output(output, loglevel) + output.split(/\n/).each do |line| + @execer.property(:returns).expects(loglevel).with(line) + end + end + + before do + @executable = Puppet.features.posix? ? '/bin/true' : 'C:/Program Files/something.exe' + @command = Puppet.features.posix? ? '/bin/true whatever' : '"C:/Program Files/something.exe" whatever' + File.stubs(:exists?).returns false + File.stubs(:exists?).with(@executable).returns true + @example_path = Puppet.features.posix? ? %w{/usr/bin /bin} : [ "C:/Program Files/something/bin", "C:/Ruby/bin" ] + File.stubs(:exists?).with(File.join(@example_path[0],"true")).returns true + File.stubs(:exists?).with(File.join(@example_path[0],"false")).returns true + end + + it "should return :executed_command as its event" do + resource = Puppet::Type.type(:exec).new :command => @command + resource.parameter(:returns).event.name.should == :executed_command + end + + describe "when execing" do + + it "should use the 'run_and_capture' method to exec" do + command = "true" + create_resource(command, "", 0) + + @execer.refresh.should == :executed_command + end + + it "should report a failure" do + command = "false" + create_resource(command, "", 1) + + proc { @execer.refresh }.should raise_error(Puppet::Error) + end + + it "should not report a failure if the exit status is specified in a returns array" do + command = "false" + create_resource(command, "", 1, [0,1]) + proc { @execer.refresh }.should_not raise_error(Puppet::Error) + end + + it "should report a failure if the exit status is not specified in a returns array" do + command = "false" + create_resource(command, "", 1, [0,100]) + proc { @execer.refresh }.should raise_error(Puppet::Error) + end + + it "should log the output on success" do + #Puppet::Util::Log.newdestination :console + command = "false" + output = "output1\noutput2\n" + create_logging_resource(command, output, 0, true, :err) + expect_output(output, :err) + @execer.refresh + end + + it "should log the output on failure" do + #Puppet::Util::Log.newdestination :console + command = "false" + output = "output1\noutput2\n" + create_logging_resource(command, output, 1, true, :err) + expect_output(output, :err) + + proc { @execer.refresh }.should raise_error(Puppet::Error) + end + + end + + describe "when logoutput=>on_failure is set" do + + it "should log the output on failure" do + #Puppet::Util::Log.newdestination :console + command = "false" + output = "output1\noutput2\n" + create_logging_resource(command, output, 1, :on_failure, :err) + expect_output(output, :err) + + proc { @execer.refresh }.should raise_error(Puppet::Error) + end + + it "should log the output on failure when returns is specified as an array" do + #Puppet::Util::Log.newdestination :console + command = "false" + output = "output1\noutput2\n" + create_logging_resource(command, output, 1, :on_failure, :err, [0, 100]) + expect_output(output, :err) + + proc { @execer.refresh }.should raise_error(Puppet::Error) + end + + it "shouldn't log the output on success" do + #Puppet::Util::Log.newdestination :console + command = "true" + output = "output1\noutput2\n" + create_logging_resource(command, output, 0, :on_failure, :err) + @execer.property(:returns).expects(:err).never + @execer.refresh + end + end + + it "shouldn't log the output on success when non-zero exit status is in a returns array" do + #Puppet::Util::Log.newdestination :console + command = "true" + output = "output1\noutput2\n" + create_logging_resource(command, output, 100, :on_failure, :err, [1,100]) + @execer.property(:returns).expects(:err).never + @execer.refresh + end + + describe " when multiple tries are set," do + + it "should repeat the command attempt 'tries' times on failure and produce an error" do + Puppet.features.stubs(:root?).returns(true) + command = "false" + user = "user" + group = "group" + tries = 5 + retry_exec = Puppet::Type.type(:exec).new(:name => command, :path => %w{/usr/bin /bin}, :user => user, :group => group, :returns => 0, :tries => tries, :try_sleep => 0) + status = stub "process" + status.stubs(:exitstatus).returns(1) + Puppet::Util::SUIDManager.expects(:run_and_capture).with([command], user, group).times(tries).returns(["", status]) + proc { retry_exec.refresh }.should raise_error(Puppet::Error) + end + end + + it "should be able to autorequire files mentioned in the command" do + catalog = Puppet::Resource::Catalog.new + catalog.add_resource Puppet::Type.type(:file).new(:name => @executable) + @execer = Puppet::Type.type(:exec).new(:name => @command) + catalog.add_resource @execer + + rels = @execer.autorequire + rels[0].should be_instance_of(Puppet::Relationship) + rels[0].target.should equal(@execer) + end +end diff --git a/spec/unit/type/exec_spec_spec.rb b/spec/unit/type/exec_spec_spec.rb deleted file mode 100755 index 1eaf80390..000000000 --- a/spec/unit/type/exec_spec_spec.rb +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Type.type(:exec) do - - def create_resource(command, output, exitstatus, returns = 0) - @user_name = 'some_user_name' - @group_name = 'some_group_name' - Puppet.features.stubs(:root?).returns(true) - @execer = Puppet::Type.type(:exec).new(:name => command, :path => @example_path, :user => @user_name, :group => @group_name, :returns => returns) - - status = stub "process" - status.stubs(:exitstatus).returns(exitstatus) - - Puppet::Util::SUIDManager.expects(:run_and_capture).with([command], @user_name, @group_name).returns([output, status]) - end - - def create_logging_resource(command, output, exitstatus, logoutput, loglevel, returns = 0) - create_resource(command, output, exitstatus, returns) - @execer[:logoutput] = logoutput - @execer[:loglevel] = loglevel - end - - def expect_output(output, loglevel) - output.split(/\n/).each do |line| - @execer.property(:returns).expects(loglevel).with(line) - end - end - - before do - @executable = Puppet.features.posix? ? '/bin/true' : 'C:/Program Files/something.exe' - @command = Puppet.features.posix? ? '/bin/true whatever' : '"C:/Program Files/something.exe" whatever' - File.stubs(:exists?).returns false - File.stubs(:exists?).with(@executable).returns true - @example_path = Puppet.features.posix? ? %w{/usr/bin /bin} : [ "C:/Program Files/something/bin", "C:/Ruby/bin" ] - File.stubs(:exists?).with(File.join(@example_path[0],"true")).returns true - File.stubs(:exists?).with(File.join(@example_path[0],"false")).returns true - end - - it "should return :executed_command as its event" do - resource = Puppet::Type.type(:exec).new :command => @command - resource.parameter(:returns).event.name.should == :executed_command - end - - describe "when execing" do - - it "should use the 'run_and_capture' method to exec" do - command = "true" - create_resource(command, "", 0) - - @execer.refresh.should == :executed_command - end - - it "should report a failure" do - command = "false" - create_resource(command, "", 1) - - proc { @execer.refresh }.should raise_error(Puppet::Error) - end - - it "should not report a failure if the exit status is specified in a returns array" do - command = "false" - create_resource(command, "", 1, [0,1]) - proc { @execer.refresh }.should_not raise_error(Puppet::Error) - end - - it "should report a failure if the exit status is not specified in a returns array" do - command = "false" - create_resource(command, "", 1, [0,100]) - proc { @execer.refresh }.should raise_error(Puppet::Error) - end - - it "should log the output on success" do - #Puppet::Util::Log.newdestination :console - command = "false" - output = "output1\noutput2\n" - create_logging_resource(command, output, 0, true, :err) - expect_output(output, :err) - @execer.refresh - end - - it "should log the output on failure" do - #Puppet::Util::Log.newdestination :console - command = "false" - output = "output1\noutput2\n" - create_logging_resource(command, output, 1, true, :err) - expect_output(output, :err) - - proc { @execer.refresh }.should raise_error(Puppet::Error) - end - - end - - describe "when logoutput=>on_failure is set" do - - it "should log the output on failure" do - #Puppet::Util::Log.newdestination :console - command = "false" - output = "output1\noutput2\n" - create_logging_resource(command, output, 1, :on_failure, :err) - expect_output(output, :err) - - proc { @execer.refresh }.should raise_error(Puppet::Error) - end - - it "should log the output on failure when returns is specified as an array" do - #Puppet::Util::Log.newdestination :console - command = "false" - output = "output1\noutput2\n" - create_logging_resource(command, output, 1, :on_failure, :err, [0, 100]) - expect_output(output, :err) - - proc { @execer.refresh }.should raise_error(Puppet::Error) - end - - it "shouldn't log the output on success" do - #Puppet::Util::Log.newdestination :console - command = "true" - output = "output1\noutput2\n" - create_logging_resource(command, output, 0, :on_failure, :err) - @execer.property(:returns).expects(:err).never - @execer.refresh - end - end - - it "shouldn't log the output on success when non-zero exit status is in a returns array" do - #Puppet::Util::Log.newdestination :console - command = "true" - output = "output1\noutput2\n" - create_logging_resource(command, output, 100, :on_failure, :err, [1,100]) - @execer.property(:returns).expects(:err).never - @execer.refresh - end - - describe " when multiple tries are set," do - - it "should repeat the command attempt 'tries' times on failure and produce an error" do - Puppet.features.stubs(:root?).returns(true) - command = "false" - user = "user" - group = "group" - tries = 5 - retry_exec = Puppet::Type.type(:exec).new(:name => command, :path => %w{/usr/bin /bin}, :user => user, :group => group, :returns => 0, :tries => tries, :try_sleep => 0) - status = stub "process" - status.stubs(:exitstatus).returns(1) - Puppet::Util::SUIDManager.expects(:run_and_capture).with([command], user, group).times(tries).returns(["", status]) - proc { retry_exec.refresh }.should raise_error(Puppet::Error) - end - end - - it "should be able to autorequire files mentioned in the command" do - catalog = Puppet::Resource::Catalog.new - catalog.add_resource Puppet::Type.type(:file).new(:name => @executable) - @execer = Puppet::Type.type(:exec).new(:name => @command) - catalog.add_resource @execer - - rels = @execer.autorequire - rels[0].should be_instance_of(Puppet::Relationship) - rels[0].target.should equal(@execer) - end -end diff --git a/spec/unit/type/file/checksum_spec.rb b/spec/unit/type/file/checksum_spec.rb new file mode 100644 index 000000000..e6a853042 --- /dev/null +++ b/spec/unit/type/file/checksum_spec.rb @@ -0,0 +1,73 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +checksum = Puppet::Type.type(:file).attrclass(:checksum) +describe checksum do + before do + @resource = Puppet::Type.type(:file).new :path => "/foo/bar" + @checksum = @resource.parameter(:checksum) + end + + it "should be a parameter" do + checksum.superclass.must == Puppet::Parameter + end + + it "should use its current value when asked to sum content" do + @checksum.value = :md5lite + @checksum.expects(:md5lite).with("foobar").returns "yay" + @checksum.sum("foobar") + end + + it "should use :md5 to sum when no value is set" do + @checksum.expects(:md5).with("foobar").returns "yay" + @checksum.sum("foobar") + end + + it "should return the summed contents with a checksum label" do + sum = Digest::MD5.hexdigest("foobar") + @resource[:checksum] = :md5 + @checksum.sum("foobar").should == "{md5}#{sum}" + end + + it "should use :md5 as its default type" do + @checksum.default.should == :md5 + end + + it "should use its current value when asked to sum a file's content" do + @checksum.value = :md5lite + @checksum.expects(:md5lite_file).with("/foo/bar").returns "yay" + @checksum.sum_file("/foo/bar") + end + + it "should use :md5 to sum a file when no value is set" do + @checksum.expects(:md5_file).with("/foo/bar").returns "yay" + @checksum.sum_file("/foo/bar") + end + + it "should convert all sums to strings when summing files" do + @checksum.value = :mtime + @checksum.expects(:mtime_file).with("/foo/bar").returns Time.now + lambda { @checksum.sum_file("/foo/bar") }.should_not raise_error + end + + it "should return the summed contents of a file with a checksum label" do + @resource[:checksum] = :md5 + @checksum.expects(:md5_file).returns "mysum" + @checksum.sum_file("/foo/bar").should == "{md5}mysum" + end + + it "should return the summed contents of a stream with a checksum label" do + @resource[:checksum] = :md5 + @checksum.expects(:md5_stream).returns "mysum" + @checksum.sum_stream.should == "{md5}mysum" + end + + it "should yield the sum_stream block to the underlying checksum" do + @resource[:checksum] = :md5 + @checksum.expects(:md5_stream).yields("something").returns("mysum") + @checksum.sum_stream do |sum| + sum.should == "something" + end + end +end diff --git a/spec/unit/type/file/checksum_spec_spec.rb b/spec/unit/type/file/checksum_spec_spec.rb deleted file mode 100644 index e6a853042..000000000 --- a/spec/unit/type/file/checksum_spec_spec.rb +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -checksum = Puppet::Type.type(:file).attrclass(:checksum) -describe checksum do - before do - @resource = Puppet::Type.type(:file).new :path => "/foo/bar" - @checksum = @resource.parameter(:checksum) - end - - it "should be a parameter" do - checksum.superclass.must == Puppet::Parameter - end - - it "should use its current value when asked to sum content" do - @checksum.value = :md5lite - @checksum.expects(:md5lite).with("foobar").returns "yay" - @checksum.sum("foobar") - end - - it "should use :md5 to sum when no value is set" do - @checksum.expects(:md5).with("foobar").returns "yay" - @checksum.sum("foobar") - end - - it "should return the summed contents with a checksum label" do - sum = Digest::MD5.hexdigest("foobar") - @resource[:checksum] = :md5 - @checksum.sum("foobar").should == "{md5}#{sum}" - end - - it "should use :md5 as its default type" do - @checksum.default.should == :md5 - end - - it "should use its current value when asked to sum a file's content" do - @checksum.value = :md5lite - @checksum.expects(:md5lite_file).with("/foo/bar").returns "yay" - @checksum.sum_file("/foo/bar") - end - - it "should use :md5 to sum a file when no value is set" do - @checksum.expects(:md5_file).with("/foo/bar").returns "yay" - @checksum.sum_file("/foo/bar") - end - - it "should convert all sums to strings when summing files" do - @checksum.value = :mtime - @checksum.expects(:mtime_file).with("/foo/bar").returns Time.now - lambda { @checksum.sum_file("/foo/bar") }.should_not raise_error - end - - it "should return the summed contents of a file with a checksum label" do - @resource[:checksum] = :md5 - @checksum.expects(:md5_file).returns "mysum" - @checksum.sum_file("/foo/bar").should == "{md5}mysum" - end - - it "should return the summed contents of a stream with a checksum label" do - @resource[:checksum] = :md5 - @checksum.expects(:md5_stream).returns "mysum" - @checksum.sum_stream.should == "{md5}mysum" - end - - it "should yield the sum_stream block to the underlying checksum" do - @resource[:checksum] = :md5 - @checksum.expects(:md5_stream).yields("something").returns("mysum") - @checksum.sum_stream do |sum| - sum.should == "something" - end - end -end diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb new file mode 100755 index 000000000..f1001aa8c --- /dev/null +++ b/spec/unit/type/file/content_spec.rb @@ -0,0 +1,459 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +content = Puppet::Type.type(:file).attrclass(:content) +describe content do + before do + @resource = Puppet::Type.type(:file).new :path => "/foo/bar" + end + + it "should be a subclass of Property" do + content.superclass.must == Puppet::Property + end + + describe "when determining the checksum type" do + it "should use the type specified in the source checksum if a source is set" do + @resource[:source] = "/foo" + @resource.parameter(:source).expects(:checksum).returns "{md5lite}eh" + + @content = content.new(:resource => @resource) + @content.checksum_type.should == :md5lite + end + + it "should use the type specified by the checksum parameter if no source is set" do + @resource[:checksum] = :md5lite + + @content = content.new(:resource => @resource) + @content.checksum_type.should == :md5lite + end + end + + describe "when determining the actual content to write" do + it "should use the set content if available" do + @content = content.new(:resource => @resource) + @content.should = "ehness" + @content.actual_content.should == "ehness" + end + + it "should not use the content from the source if the source is set" do + source = mock 'source' + + @resource.expects(:parameter).never.with(:source).returns source + + @content = content.new(:resource => @resource) + @content.actual_content.should be_nil + end + end + + describe "when setting the desired content" do + it "should make the actual content available via an attribute" do + @content = content.new(:resource => @resource) + @content.stubs(:checksum_type).returns "md5" + @content.should = "this is some content" + + @content.actual_content.should == "this is some content" + end + + it "should store the checksum as the desired content" do + @content = content.new(:resource => @resource) + digest = Digest::MD5.hexdigest("this is some content") + + @content.stubs(:checksum_type).returns "md5" + @content.should = "this is some content" + + @content.should.must == "{md5}#{digest}" + end + + it "should not checksum 'absent'" do + @content = content.new(:resource => @resource) + @content.should = :absent + + @content.should.must == :absent + end + + it "should accept a checksum as the desired content" do + @content = content.new(:resource => @resource) + digest = Digest::MD5.hexdigest("this is some content") + + string = "{md5}#{digest}" + @content.should = string + + @content.should.must == string + end + end + + describe "when retrieving the current content" do + it "should return :absent if the file does not exist" do + @content = content.new(:resource => @resource) + @resource.expects(:stat).returns nil + + @content.retrieve.should == :absent + end + + it "should not manage content on directories" do + @content = content.new(:resource => @resource) + + stat = mock 'stat', :ftype => "directory" + @resource.expects(:stat).returns stat + + @content.retrieve.should be_nil + end + + it "should not manage content on links" do + @content = content.new(:resource => @resource) + + stat = mock 'stat', :ftype => "link" + @resource.expects(:stat).returns stat + + @content.retrieve.should be_nil + end + + it "should always return the checksum as a string" do + @content = content.new(:resource => @resource) + @resource[:checksum] = :mtime + + stat = mock 'stat', :ftype => "file" + @resource.expects(:stat).returns stat + + time = Time.now + @resource.parameter(:checksum).expects(:mtime_file).with(@resource[:path]).returns time + + @content.retrieve.should == "{mtime}%s" % time + end + + it "should return the checksum of the file if it exists and is a normal file" do + @content = content.new(:resource => @resource) + stat = mock 'stat', :ftype => "file" + @resource.expects(:stat).returns stat + @resource.parameter(:checksum).expects(:md5_file).with(@resource[:path]).returns "mysum" + + @content.retrieve.should == "{md5}mysum" + end + end + + describe "when testing whether the content is in sync" do + before do + @resource[:ensure] = :file + @content = content.new(:resource => @resource) + end + + it "should return true if the resource shouldn't be a regular file" do + @resource.expects(:should_be_file?).returns false + @content.must be_insync("whatever") + end + + it "should return false if the current content is :absent" do + @content.should_not be_insync(:absent) + end + + it "should return false if the file should be a file but is not present" do + @resource.expects(:should_be_file?).returns true + + @content.should_not be_insync(:absent) + end + + describe "and the file exists" do + before do + @resource.stubs(:stat).returns mock("stat") + end + + it "should return false if the current contents are different from the desired content" do + @content.should = "some content" + @content.should_not be_insync("other content") + end + + it "should return true if the sum for the current contents is the same as the sum for the desired content" do + @content.should = "some content" + @content.must be_insync("{md5}" + Digest::MD5.hexdigest("some content")) + end + + describe "and Puppet[:show_diff] is set" do + before do + Puppet[:show_diff] = true + end + + it "should display a diff if the current contents are different from the desired content" do + @content.should = "some content" + @content.expects(:string_file_diff).once + + @content.insync?("other content") + end + + it "should not display a diff if the sum for the current contents is the same as the sum for the desired content" do + @content.should = "some content" + @content.expects(:string_file_diff).never + + @content.insync?("{md5}" + Digest::MD5.hexdigest("some content")) + end + end + end + + describe "and :replace is false" do + before do + @resource.stubs(:replace?).returns false + end + + it "should be insync if the file exists and the content is different" do + @resource.stubs(:stat).returns mock('stat') + + @content.must be_insync("whatever") + end + + it "should be insync if the file exists and the content is right" do + @resource.stubs(:stat).returns mock('stat') + + @content.must be_insync("something") + end + + it "should not be insync if the file does not exist" do + @content.should_not be_insync(:absent) + end + end + end + + describe "when changing the content" do + before do + @content = content.new(:resource => @resource) + @content.should = "some content" + + @resource.stubs(:[]).with(:path).returns "/boo" + @resource.stubs(:stat).returns "eh" + end + + it "should use the file's :write method to write the content" do + @resource.expects(:write).with(:content) + + @content.sync + end + + it "should return :file_changed if the file already existed" do + @resource.expects(:stat).returns "something" + @resource.stubs(:write) + @content.sync.should == :file_changed + end + + it "should return :file_created if the file did not exist" do + @resource.expects(:stat).returns nil + @resource.stubs(:write) + @content.sync.should == :file_created + end + end + + describe "when writing" do + before do + @content = content.new(:resource => @resource) + @fh = stub_everything + end + + it "should attempt to read from the filebucket if no actual content nor source exists" do + @content.should = "{md5}foo" + @content.resource.bucket.class.any_instance.stubs(:getfile).returns "foo" + @content.write(@fh) + end + + describe "from actual content" do + before(:each) do + @content.stubs(:actual_content).returns("this is content") + end + + it "should write to the given file handle" do + @fh.expects(:print).with("this is content") + @content.write(@fh) + end + + it "should return the current checksum value" do + @resource.parameter(:checksum).expects(:sum_stream).returns "checksum" + @content.write(@fh).should == "checksum" + end + end + + describe "from a file bucket" do + it "should fail if a file bucket cannot be retrieved" do + @content.should = "{md5}foo" + @content.resource.expects(:bucket).returns nil + lambda { @content.write(@fh) }.should raise_error(Puppet::Error) + end + + it "should fail if the file bucket cannot find any content" do + @content.should = "{md5}foo" + bucket = stub 'bucket' + @content.resource.expects(:bucket).returns bucket + bucket.expects(:getfile).with("foo").raises "foobar" + lambda { @content.write(@fh) }.should raise_error(Puppet::Error) + end + + it "should write the returned content to the file" do + @content.should = "{md5}foo" + bucket = stub 'bucket' + @content.resource.expects(:bucket).returns bucket + bucket.expects(:getfile).with("foo").returns "mycontent" + + @fh.expects(:print).with("mycontent") + @content.write(@fh) + end + end + + describe "from local source" do + before(:each) do + @content.stubs(:actual_content).returns(nil) + @source = stub_everything 'source', :local? => true, :full_path => "/path/to/source" + @resource.stubs(:parameter).with(:source).returns @source + + @sum = stub_everything 'sum' + @resource.stubs(:parameter).with(:checksum).returns(@sum) + + @digest = stub_everything 'digest' + @sum.stubs(:sum_stream).yields(@digest) + + @file = stub_everything 'file' + File.stubs(:open).yields(@file) + @file.stubs(:read).with(8192).returns("chunk1").then.returns("chunk2").then.returns(nil) + end + + it "should open the local file" do + File.expects(:open).with("/path/to/source", "r") + @content.write(@fh) + end + + it "should read the local file by chunks" do + @file.expects(:read).with(8192).returns("chunk1").then.returns(nil) + @content.write(@fh) + end + + it "should write each chunk to the file" do + @fh.expects(:print).with("chunk1").then.with("chunk2") + @content.write(@fh) + end + + it "should pass each chunk to the current sum stream" do + @digest.expects(:<<).with("chunk1").then.with("chunk2") + @content.write(@fh) + end + + it "should return the checksum computed" do + @sum.stubs(:sum_stream).yields(@digest).returns("checksum") + @content.write(@fh).should == "checksum" + end + end + + describe "from remote source" do + before(:each) do + @response = stub_everything 'mock response', :code => "404" + @conn = stub_everything 'connection' + @conn.stubs(:request_get).yields(@response) + Puppet::Network::HttpPool.stubs(:http_instance).returns @conn + + @content.stubs(:actual_content).returns(nil) + @source = stub_everything 'source', :local? => false, :full_path => "/path/to/source", :server => "server", :port => 1234 + @resource.stubs(:parameter).with(:source).returns @source + + @sum = stub_everything 'sum' + @resource.stubs(:parameter).with(:checksum).returns(@sum) + + @digest = stub_everything 'digest' + @sum.stubs(:sum_stream).yields(@digest) + end + + it "should open a network connection to source server and port" do + Puppet::Network::HttpPool.expects(:http_instance).with("server", 1234).returns @conn + @content.write(@fh) + end + + it "should send the correct indirection uri" do + @conn.expects(:request_get).with { |uri,headers| uri == "/production/file_content//path/to/source" }.yields(@response) + @content.write(@fh) + end + + it "should return nil if source is not found" do + @response.expects(:code).returns("404") + @content.write(@fh).should == nil + end + + it "should not write anything if source is not found" do + @response.expects(:code).returns("404") + @fh.expects(:print).never + @content.write(@fh).should == nil + end + + it "should raise an HTTP error in case of server error" do + @response.expects(:code).returns("500") + lambda { @content.write(@fh) }.should raise_error + end + + it "should write content by chunks" do + @response.expects(:code).returns("200") + @response.expects(:read_body).multiple_yields("chunk1","chunk2") + @fh.expects(:print).with("chunk1").then.with("chunk2") + @content.write(@fh) + end + + it "should pass each chunk to the current sum stream" do + @response.expects(:code).returns("200") + @response.expects(:read_body).multiple_yields("chunk1","chunk2") + @digest.expects(:<<).with("chunk1").then.with("chunk2") + @content.write(@fh) + end + + it "should return the checksum computed" do + @response.expects(:code).returns("200") + @response.expects(:read_body).multiple_yields("chunk1","chunk2") + @sum.expects(:sum_stream).yields(@digest).returns("checksum") + @content.write(@fh).should == "checksum" + end + + it "should get the current accept encoding header value" do + @content.expects(:add_accept_encoding) + @content.write(@fh) + end + + it "should uncompress body on error" do + @response.expects(:code).returns("500") + @response.expects(:body).returns("compressed body") + @content.expects(:uncompress_body).with(@response).returns("uncompressed") + lambda { @content.write(@fh) }.should raise_error { |e| e.message =~ /uncompressed/ } + end + + it "should uncompress chunk by chunk" do + uncompressor = stub_everything 'uncompressor' + @content.expects(:uncompress).with(@response).yields(uncompressor) + @response.expects(:code).returns("200") + @response.expects(:read_body).multiple_yields("chunk1","chunk2") + + uncompressor.expects(:uncompress).with("chunk1").then.with("chunk2") + @content.write(@fh) + end + + it "should write uncompressed chunks to the file" do + uncompressor = stub_everything 'uncompressor' + @content.expects(:uncompress).with(@response).yields(uncompressor) + @response.expects(:code).returns("200") + @response.expects(:read_body).multiple_yields("chunk1","chunk2") + + uncompressor.expects(:uncompress).with("chunk1").returns("uncompressed1") + uncompressor.expects(:uncompress).with("chunk2").returns("uncompressed2") + + @fh.expects(:print).with("uncompressed1") + @fh.expects(:print).with("uncompressed2") + + @content.write(@fh) + end + + it "should pass each uncompressed chunk to the current sum stream" do + uncompressor = stub_everything 'uncompressor' + @content.expects(:uncompress).with(@response).yields(uncompressor) + @response.expects(:code).returns("200") + @response.expects(:read_body).multiple_yields("chunk1","chunk2") + + uncompressor.expects(:uncompress).with("chunk1").returns("uncompressed1") + uncompressor.expects(:uncompress).with("chunk2").returns("uncompressed2") + + @digest.expects(:<<).with("uncompressed1").then.with("uncompressed2") + @content.write(@fh) + end + end + + describe "from a filebucket" do + end + end +end diff --git a/spec/unit/type/file/content_spec_spec.rb b/spec/unit/type/file/content_spec_spec.rb deleted file mode 100755 index f1001aa8c..000000000 --- a/spec/unit/type/file/content_spec_spec.rb +++ /dev/null @@ -1,459 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -content = Puppet::Type.type(:file).attrclass(:content) -describe content do - before do - @resource = Puppet::Type.type(:file).new :path => "/foo/bar" - end - - it "should be a subclass of Property" do - content.superclass.must == Puppet::Property - end - - describe "when determining the checksum type" do - it "should use the type specified in the source checksum if a source is set" do - @resource[:source] = "/foo" - @resource.parameter(:source).expects(:checksum).returns "{md5lite}eh" - - @content = content.new(:resource => @resource) - @content.checksum_type.should == :md5lite - end - - it "should use the type specified by the checksum parameter if no source is set" do - @resource[:checksum] = :md5lite - - @content = content.new(:resource => @resource) - @content.checksum_type.should == :md5lite - end - end - - describe "when determining the actual content to write" do - it "should use the set content if available" do - @content = content.new(:resource => @resource) - @content.should = "ehness" - @content.actual_content.should == "ehness" - end - - it "should not use the content from the source if the source is set" do - source = mock 'source' - - @resource.expects(:parameter).never.with(:source).returns source - - @content = content.new(:resource => @resource) - @content.actual_content.should be_nil - end - end - - describe "when setting the desired content" do - it "should make the actual content available via an attribute" do - @content = content.new(:resource => @resource) - @content.stubs(:checksum_type).returns "md5" - @content.should = "this is some content" - - @content.actual_content.should == "this is some content" - end - - it "should store the checksum as the desired content" do - @content = content.new(:resource => @resource) - digest = Digest::MD5.hexdigest("this is some content") - - @content.stubs(:checksum_type).returns "md5" - @content.should = "this is some content" - - @content.should.must == "{md5}#{digest}" - end - - it "should not checksum 'absent'" do - @content = content.new(:resource => @resource) - @content.should = :absent - - @content.should.must == :absent - end - - it "should accept a checksum as the desired content" do - @content = content.new(:resource => @resource) - digest = Digest::MD5.hexdigest("this is some content") - - string = "{md5}#{digest}" - @content.should = string - - @content.should.must == string - end - end - - describe "when retrieving the current content" do - it "should return :absent if the file does not exist" do - @content = content.new(:resource => @resource) - @resource.expects(:stat).returns nil - - @content.retrieve.should == :absent - end - - it "should not manage content on directories" do - @content = content.new(:resource => @resource) - - stat = mock 'stat', :ftype => "directory" - @resource.expects(:stat).returns stat - - @content.retrieve.should be_nil - end - - it "should not manage content on links" do - @content = content.new(:resource => @resource) - - stat = mock 'stat', :ftype => "link" - @resource.expects(:stat).returns stat - - @content.retrieve.should be_nil - end - - it "should always return the checksum as a string" do - @content = content.new(:resource => @resource) - @resource[:checksum] = :mtime - - stat = mock 'stat', :ftype => "file" - @resource.expects(:stat).returns stat - - time = Time.now - @resource.parameter(:checksum).expects(:mtime_file).with(@resource[:path]).returns time - - @content.retrieve.should == "{mtime}%s" % time - end - - it "should return the checksum of the file if it exists and is a normal file" do - @content = content.new(:resource => @resource) - stat = mock 'stat', :ftype => "file" - @resource.expects(:stat).returns stat - @resource.parameter(:checksum).expects(:md5_file).with(@resource[:path]).returns "mysum" - - @content.retrieve.should == "{md5}mysum" - end - end - - describe "when testing whether the content is in sync" do - before do - @resource[:ensure] = :file - @content = content.new(:resource => @resource) - end - - it "should return true if the resource shouldn't be a regular file" do - @resource.expects(:should_be_file?).returns false - @content.must be_insync("whatever") - end - - it "should return false if the current content is :absent" do - @content.should_not be_insync(:absent) - end - - it "should return false if the file should be a file but is not present" do - @resource.expects(:should_be_file?).returns true - - @content.should_not be_insync(:absent) - end - - describe "and the file exists" do - before do - @resource.stubs(:stat).returns mock("stat") - end - - it "should return false if the current contents are different from the desired content" do - @content.should = "some content" - @content.should_not be_insync("other content") - end - - it "should return true if the sum for the current contents is the same as the sum for the desired content" do - @content.should = "some content" - @content.must be_insync("{md5}" + Digest::MD5.hexdigest("some content")) - end - - describe "and Puppet[:show_diff] is set" do - before do - Puppet[:show_diff] = true - end - - it "should display a diff if the current contents are different from the desired content" do - @content.should = "some content" - @content.expects(:string_file_diff).once - - @content.insync?("other content") - end - - it "should not display a diff if the sum for the current contents is the same as the sum for the desired content" do - @content.should = "some content" - @content.expects(:string_file_diff).never - - @content.insync?("{md5}" + Digest::MD5.hexdigest("some content")) - end - end - end - - describe "and :replace is false" do - before do - @resource.stubs(:replace?).returns false - end - - it "should be insync if the file exists and the content is different" do - @resource.stubs(:stat).returns mock('stat') - - @content.must be_insync("whatever") - end - - it "should be insync if the file exists and the content is right" do - @resource.stubs(:stat).returns mock('stat') - - @content.must be_insync("something") - end - - it "should not be insync if the file does not exist" do - @content.should_not be_insync(:absent) - end - end - end - - describe "when changing the content" do - before do - @content = content.new(:resource => @resource) - @content.should = "some content" - - @resource.stubs(:[]).with(:path).returns "/boo" - @resource.stubs(:stat).returns "eh" - end - - it "should use the file's :write method to write the content" do - @resource.expects(:write).with(:content) - - @content.sync - end - - it "should return :file_changed if the file already existed" do - @resource.expects(:stat).returns "something" - @resource.stubs(:write) - @content.sync.should == :file_changed - end - - it "should return :file_created if the file did not exist" do - @resource.expects(:stat).returns nil - @resource.stubs(:write) - @content.sync.should == :file_created - end - end - - describe "when writing" do - before do - @content = content.new(:resource => @resource) - @fh = stub_everything - end - - it "should attempt to read from the filebucket if no actual content nor source exists" do - @content.should = "{md5}foo" - @content.resource.bucket.class.any_instance.stubs(:getfile).returns "foo" - @content.write(@fh) - end - - describe "from actual content" do - before(:each) do - @content.stubs(:actual_content).returns("this is content") - end - - it "should write to the given file handle" do - @fh.expects(:print).with("this is content") - @content.write(@fh) - end - - it "should return the current checksum value" do - @resource.parameter(:checksum).expects(:sum_stream).returns "checksum" - @content.write(@fh).should == "checksum" - end - end - - describe "from a file bucket" do - it "should fail if a file bucket cannot be retrieved" do - @content.should = "{md5}foo" - @content.resource.expects(:bucket).returns nil - lambda { @content.write(@fh) }.should raise_error(Puppet::Error) - end - - it "should fail if the file bucket cannot find any content" do - @content.should = "{md5}foo" - bucket = stub 'bucket' - @content.resource.expects(:bucket).returns bucket - bucket.expects(:getfile).with("foo").raises "foobar" - lambda { @content.write(@fh) }.should raise_error(Puppet::Error) - end - - it "should write the returned content to the file" do - @content.should = "{md5}foo" - bucket = stub 'bucket' - @content.resource.expects(:bucket).returns bucket - bucket.expects(:getfile).with("foo").returns "mycontent" - - @fh.expects(:print).with("mycontent") - @content.write(@fh) - end - end - - describe "from local source" do - before(:each) do - @content.stubs(:actual_content).returns(nil) - @source = stub_everything 'source', :local? => true, :full_path => "/path/to/source" - @resource.stubs(:parameter).with(:source).returns @source - - @sum = stub_everything 'sum' - @resource.stubs(:parameter).with(:checksum).returns(@sum) - - @digest = stub_everything 'digest' - @sum.stubs(:sum_stream).yields(@digest) - - @file = stub_everything 'file' - File.stubs(:open).yields(@file) - @file.stubs(:read).with(8192).returns("chunk1").then.returns("chunk2").then.returns(nil) - end - - it "should open the local file" do - File.expects(:open).with("/path/to/source", "r") - @content.write(@fh) - end - - it "should read the local file by chunks" do - @file.expects(:read).with(8192).returns("chunk1").then.returns(nil) - @content.write(@fh) - end - - it "should write each chunk to the file" do - @fh.expects(:print).with("chunk1").then.with("chunk2") - @content.write(@fh) - end - - it "should pass each chunk to the current sum stream" do - @digest.expects(:<<).with("chunk1").then.with("chunk2") - @content.write(@fh) - end - - it "should return the checksum computed" do - @sum.stubs(:sum_stream).yields(@digest).returns("checksum") - @content.write(@fh).should == "checksum" - end - end - - describe "from remote source" do - before(:each) do - @response = stub_everything 'mock response', :code => "404" - @conn = stub_everything 'connection' - @conn.stubs(:request_get).yields(@response) - Puppet::Network::HttpPool.stubs(:http_instance).returns @conn - - @content.stubs(:actual_content).returns(nil) - @source = stub_everything 'source', :local? => false, :full_path => "/path/to/source", :server => "server", :port => 1234 - @resource.stubs(:parameter).with(:source).returns @source - - @sum = stub_everything 'sum' - @resource.stubs(:parameter).with(:checksum).returns(@sum) - - @digest = stub_everything 'digest' - @sum.stubs(:sum_stream).yields(@digest) - end - - it "should open a network connection to source server and port" do - Puppet::Network::HttpPool.expects(:http_instance).with("server", 1234).returns @conn - @content.write(@fh) - end - - it "should send the correct indirection uri" do - @conn.expects(:request_get).with { |uri,headers| uri == "/production/file_content//path/to/source" }.yields(@response) - @content.write(@fh) - end - - it "should return nil if source is not found" do - @response.expects(:code).returns("404") - @content.write(@fh).should == nil - end - - it "should not write anything if source is not found" do - @response.expects(:code).returns("404") - @fh.expects(:print).never - @content.write(@fh).should == nil - end - - it "should raise an HTTP error in case of server error" do - @response.expects(:code).returns("500") - lambda { @content.write(@fh) }.should raise_error - end - - it "should write content by chunks" do - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - @fh.expects(:print).with("chunk1").then.with("chunk2") - @content.write(@fh) - end - - it "should pass each chunk to the current sum stream" do - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - @digest.expects(:<<).with("chunk1").then.with("chunk2") - @content.write(@fh) - end - - it "should return the checksum computed" do - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - @sum.expects(:sum_stream).yields(@digest).returns("checksum") - @content.write(@fh).should == "checksum" - end - - it "should get the current accept encoding header value" do - @content.expects(:add_accept_encoding) - @content.write(@fh) - end - - it "should uncompress body on error" do - @response.expects(:code).returns("500") - @response.expects(:body).returns("compressed body") - @content.expects(:uncompress_body).with(@response).returns("uncompressed") - lambda { @content.write(@fh) }.should raise_error { |e| e.message =~ /uncompressed/ } - end - - it "should uncompress chunk by chunk" do - uncompressor = stub_everything 'uncompressor' - @content.expects(:uncompress).with(@response).yields(uncompressor) - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - - uncompressor.expects(:uncompress).with("chunk1").then.with("chunk2") - @content.write(@fh) - end - - it "should write uncompressed chunks to the file" do - uncompressor = stub_everything 'uncompressor' - @content.expects(:uncompress).with(@response).yields(uncompressor) - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - - uncompressor.expects(:uncompress).with("chunk1").returns("uncompressed1") - uncompressor.expects(:uncompress).with("chunk2").returns("uncompressed2") - - @fh.expects(:print).with("uncompressed1") - @fh.expects(:print).with("uncompressed2") - - @content.write(@fh) - end - - it "should pass each uncompressed chunk to the current sum stream" do - uncompressor = stub_everything 'uncompressor' - @content.expects(:uncompress).with(@response).yields(uncompressor) - @response.expects(:code).returns("200") - @response.expects(:read_body).multiple_yields("chunk1","chunk2") - - uncompressor.expects(:uncompress).with("chunk1").returns("uncompressed1") - uncompressor.expects(:uncompress).with("chunk2").returns("uncompressed2") - - @digest.expects(:<<).with("uncompressed1").then.with("uncompressed2") - @content.write(@fh) - end - end - - describe "from a filebucket" do - end - end -end diff --git a/spec/unit/type/file/ensure_spec.rb b/spec/unit/type/file/ensure_spec.rb new file mode 100755 index 000000000..d766eeb35 --- /dev/null +++ b/spec/unit/type/file/ensure_spec.rb @@ -0,0 +1,84 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +property = Puppet::Type.type(:file).attrclass(:ensure) + +describe property do + before do + # Wow that's a messy interface to the resource. + @resource = stub 'resource', :[] => nil, :[]= => nil, :property => nil, :newattr => nil, :parameter => nil, :replace? => true + @resource.stubs(:[]).returns "foo" + @resource.stubs(:[]).with(:path).returns "/my/file" + @ensure = property.new :resource => @resource + end + + it "should be a subclass of Ensure" do + property.superclass.must == Puppet::Property::Ensure + end + + describe "when retrieving the current state" do + it "should return :absent if the file does not exist" do + @ensure = property.new(:resource => @resource) + @resource.expects(:stat).returns nil + + @ensure.retrieve.should == :absent + end + + it "should return the current file type if the file exists" do + @ensure = property.new(:resource => @resource) + stat = mock 'stat', :ftype => "directory" + @resource.expects(:stat).returns stat + + @ensure.retrieve.should == :directory + end + end + + describe "when testing whether :ensure is in sync" do + before do + @ensure = property.new(:resource => @resource) + @stat = stub 'stat', :ftype => "file" + end + + it "should always be in sync if replace is 'false' unless the file is missing" do + @resource.expects(:replace?).returns false + @ensure.insync?(:link).should be_true + end + + it "should be in sync if :ensure is set to :absent and the file does not exist" do + @ensure.should = :absent + + @ensure.must be_insync(:absent) + end + + it "should not be in sync if :ensure is set to :absent and the file exists" do + @ensure.should = :absent + + @ensure.should_not be_insync(:file) + end + + it "should be in sync if a normal file exists and :ensure is set to :present" do + @ensure.should = :present + + @ensure.must be_insync(:file) + end + + it "should be in sync if a directory exists and :ensure is set to :present" do + @ensure.should = :present + + @ensure.must be_insync(:directory) + end + + it "should be in sync if a symlink exists and :ensure is set to :present" do + @ensure.should = :present + + @ensure.must be_insync(:link) + end + + it "should not be in sync if :ensure is set to :file and a directory exists" do + @ensure.should = :file + + @ensure.should_not be_insync(:directory) + end + end +end diff --git a/spec/unit/type/file/ensure_spec_spec.rb b/spec/unit/type/file/ensure_spec_spec.rb deleted file mode 100755 index d766eeb35..000000000 --- a/spec/unit/type/file/ensure_spec_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -property = Puppet::Type.type(:file).attrclass(:ensure) - -describe property do - before do - # Wow that's a messy interface to the resource. - @resource = stub 'resource', :[] => nil, :[]= => nil, :property => nil, :newattr => nil, :parameter => nil, :replace? => true - @resource.stubs(:[]).returns "foo" - @resource.stubs(:[]).with(:path).returns "/my/file" - @ensure = property.new :resource => @resource - end - - it "should be a subclass of Ensure" do - property.superclass.must == Puppet::Property::Ensure - end - - describe "when retrieving the current state" do - it "should return :absent if the file does not exist" do - @ensure = property.new(:resource => @resource) - @resource.expects(:stat).returns nil - - @ensure.retrieve.should == :absent - end - - it "should return the current file type if the file exists" do - @ensure = property.new(:resource => @resource) - stat = mock 'stat', :ftype => "directory" - @resource.expects(:stat).returns stat - - @ensure.retrieve.should == :directory - end - end - - describe "when testing whether :ensure is in sync" do - before do - @ensure = property.new(:resource => @resource) - @stat = stub 'stat', :ftype => "file" - end - - it "should always be in sync if replace is 'false' unless the file is missing" do - @resource.expects(:replace?).returns false - @ensure.insync?(:link).should be_true - end - - it "should be in sync if :ensure is set to :absent and the file does not exist" do - @ensure.should = :absent - - @ensure.must be_insync(:absent) - end - - it "should not be in sync if :ensure is set to :absent and the file exists" do - @ensure.should = :absent - - @ensure.should_not be_insync(:file) - end - - it "should be in sync if a normal file exists and :ensure is set to :present" do - @ensure.should = :present - - @ensure.must be_insync(:file) - end - - it "should be in sync if a directory exists and :ensure is set to :present" do - @ensure.should = :present - - @ensure.must be_insync(:directory) - end - - it "should be in sync if a symlink exists and :ensure is set to :present" do - @ensure.should = :present - - @ensure.must be_insync(:link) - end - - it "should not be in sync if :ensure is set to :file and a directory exists" do - @ensure.should = :file - - @ensure.should_not be_insync(:directory) - end - end -end diff --git a/spec/unit/type/file/group_spec.rb b/spec/unit/type/file/group_spec.rb new file mode 100755 index 000000000..93d09adb4 --- /dev/null +++ b/spec/unit/type/file/group_spec.rb @@ -0,0 +1,123 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +property = Puppet::Type.type(:file).attrclass(:group) + +describe property do + before do + @resource = stub 'resource', :line => "foo", :file => "bar" + @resource.stubs(:[]).returns "foo" + @resource.stubs(:[]).with(:path).returns "/my/file" + @group = property.new :resource => @resource + end + + it "should have a method for testing whether a group is valid" do + @group.must respond_to(:validgroup?) + end + + it "should return the found gid if a group is valid" do + @group.expects(:gid).with("foo").returns 500 + @group.validgroup?("foo").should == 500 + end + + it "should return false if a group is not valid" do + @group.expects(:gid).with("foo").returns nil + @group.validgroup?("foo").should be_false + end + + describe "when retrieving the current value" do + it "should return :absent if the file cannot stat" do + @resource.expects(:stat).returns nil + + @group.retrieve.should == :absent + end + + it "should get the gid from the stat instance from the file" do + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + stat.expects(:gid).returns 500 + + @group.retrieve.should == 500 + end + + it "should warn and return :silly if the found value is higher than the maximum uid value" do + Puppet.settings.expects(:value).with(:maximum_uid).returns 500 + + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + stat.expects(:gid).returns 1000 + + @group.expects(:warning) + @group.retrieve.should == :silly + end + end + + describe "when determining if the file is in sync" do + it "should directly compare the group values if the desired group is an integer" do + @group.should = [10] + @group.must be_insync(10) + end + + it "should treat numeric strings as integers" do + @group.should = ["10"] + @group.must be_insync(10) + end + + it "should convert the group name to an integer if the desired group is a string" do + @group.expects(:gid).with("foo").returns 10 + @group.should = %w{foo} + + @group.must be_insync(10) + end + + it "should not validate that groups exist when a group is specified as an integer" do + @group.expects(:gid).never + @group.validgroup?(10) + end + + it "should fail if it cannot convert a group name to an integer" do + @group.expects(:gid).with("foo").returns nil + @group.should = %w{foo} + + lambda { @group.insync?(10) }.should raise_error(Puppet::Error) + end + + it "should return false if the groups are not equal" do + @group.should = [10] + @group.should_not be_insync(20) + end + end + + describe "when changing the group" do + before do + @group.should = %w{one} + @group.stubs(:gid).returns 500 + end + + it "should chown the file if :links is set to :follow" do + @resource.expects(:[]).with(:links).returns :follow + File.expects(:chown) + + @group.sync + end + + it "should lchown the file if :links is set to :manage" do + @resource.expects(:[]).with(:links).returns :manage + File.expects(:lchown) + + @group.sync + end + + it "should use the first valid group in its 'should' list" do + @group.should = %w{one two three} + @group.expects(:validgroup?).with("one").returns nil + @group.expects(:validgroup?).with("two").returns 500 + @group.expects(:validgroup?).with("three").never + + File.expects(:chown).with(nil, 500, "/my/file") + + @group.sync + end + end +end diff --git a/spec/unit/type/file/group_spec_spec.rb b/spec/unit/type/file/group_spec_spec.rb deleted file mode 100755 index 93d09adb4..000000000 --- a/spec/unit/type/file/group_spec_spec.rb +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -property = Puppet::Type.type(:file).attrclass(:group) - -describe property do - before do - @resource = stub 'resource', :line => "foo", :file => "bar" - @resource.stubs(:[]).returns "foo" - @resource.stubs(:[]).with(:path).returns "/my/file" - @group = property.new :resource => @resource - end - - it "should have a method for testing whether a group is valid" do - @group.must respond_to(:validgroup?) - end - - it "should return the found gid if a group is valid" do - @group.expects(:gid).with("foo").returns 500 - @group.validgroup?("foo").should == 500 - end - - it "should return false if a group is not valid" do - @group.expects(:gid).with("foo").returns nil - @group.validgroup?("foo").should be_false - end - - describe "when retrieving the current value" do - it "should return :absent if the file cannot stat" do - @resource.expects(:stat).returns nil - - @group.retrieve.should == :absent - end - - it "should get the gid from the stat instance from the file" do - stat = stub 'stat', :ftype => "foo" - @resource.expects(:stat).returns stat - stat.expects(:gid).returns 500 - - @group.retrieve.should == 500 - end - - it "should warn and return :silly if the found value is higher than the maximum uid value" do - Puppet.settings.expects(:value).with(:maximum_uid).returns 500 - - stat = stub 'stat', :ftype => "foo" - @resource.expects(:stat).returns stat - stat.expects(:gid).returns 1000 - - @group.expects(:warning) - @group.retrieve.should == :silly - end - end - - describe "when determining if the file is in sync" do - it "should directly compare the group values if the desired group is an integer" do - @group.should = [10] - @group.must be_insync(10) - end - - it "should treat numeric strings as integers" do - @group.should = ["10"] - @group.must be_insync(10) - end - - it "should convert the group name to an integer if the desired group is a string" do - @group.expects(:gid).with("foo").returns 10 - @group.should = %w{foo} - - @group.must be_insync(10) - end - - it "should not validate that groups exist when a group is specified as an integer" do - @group.expects(:gid).never - @group.validgroup?(10) - end - - it "should fail if it cannot convert a group name to an integer" do - @group.expects(:gid).with("foo").returns nil - @group.should = %w{foo} - - lambda { @group.insync?(10) }.should raise_error(Puppet::Error) - end - - it "should return false if the groups are not equal" do - @group.should = [10] - @group.should_not be_insync(20) - end - end - - describe "when changing the group" do - before do - @group.should = %w{one} - @group.stubs(:gid).returns 500 - end - - it "should chown the file if :links is set to :follow" do - @resource.expects(:[]).with(:links).returns :follow - File.expects(:chown) - - @group.sync - end - - it "should lchown the file if :links is set to :manage" do - @resource.expects(:[]).with(:links).returns :manage - File.expects(:lchown) - - @group.sync - end - - it "should use the first valid group in its 'should' list" do - @group.should = %w{one two three} - @group.expects(:validgroup?).with("one").returns nil - @group.expects(:validgroup?).with("two").returns 500 - @group.expects(:validgroup?).with("three").never - - File.expects(:chown).with(nil, 500, "/my/file") - - @group.sync - end - end -end diff --git a/spec/unit/type/file/owner_spec.rb b/spec/unit/type/file/owner_spec.rb new file mode 100755 index 000000000..a077ccec2 --- /dev/null +++ b/spec/unit/type/file/owner_spec.rb @@ -0,0 +1,150 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +property = Puppet::Type.type(:file).attrclass(:owner) + +describe property do + before do + # FIXME: many of these tests exercise the provider rather than `owner` + # and should be moved into provider tests. ~JW + @provider = Puppet::Type.type(:file).provider(:posix).new + @provider.stubs(:uid).with("one").returns(1) + + @resource = stub 'resource', :line => "foo", :file => "bar" + @resource.stubs(:[]).returns "foo" + @resource.stubs(:[]).with(:path).returns "/my/file" + @resource.stubs(:provider).returns @provider + + @owner = property.new :resource => @resource + end + + it "should have a method for testing whether an owner is valid" do + @provider.must respond_to(:validuser?) + end + + it "should return the found uid if an owner is valid" do + @provider.expects(:uid).with("foo").returns 500 + @provider.validuser?("foo").should == 500 + end + + it "should return false if an owner is not valid" do + @provider.expects(:uid).with("foo").returns nil + @provider.validuser?("foo").should be_false + end + + describe "when retrieving the current value" do + it "should return :absent if the file cannot stat" do + @resource.expects(:stat).returns nil + + @owner.retrieve.should == :absent + end + + it "should get the uid from the stat instance from the file" do + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + stat.expects(:uid).returns 500 + + @owner.retrieve.should == 500 + end + + it "should warn and return :silly if the found value is higher than the maximum uid value" do + Puppet.settings.expects(:value).with(:maximum_uid).returns 500 + + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + stat.expects(:uid).returns 1000 + + @provider.expects(:warning) + @owner.retrieve.should == :silly + end + end + + describe "when determining if the file is in sync" do + describe "and not running as root" do + it "should warn once and return true" do + Puppet.features.expects(:root?).returns false + + @provider.expects(:warnonce) + + @owner.should = [10] + @owner.must be_insync(20) + end + end + + before do + Puppet.features.stubs(:root?).returns true + end + + it "should be in sync if 'should' is not provided" do + @owner.must be_insync(10) + end + + it "should directly compare the owner values if the desired owner is an integer" do + @owner.should = [10] + @owner.must be_insync(10) + end + + it "should treat numeric strings as integers" do + @owner.should = ["10"] + @owner.must be_insync(10) + end + + it "should convert the owner name to an integer if the desired owner is a string" do + @provider.expects(:uid).with("foo").returns 10 + @owner.should = %w{foo} + + @owner.must be_insync(10) + end + + it "should not validate that users exist when a user is specified as an integer" do + @provider.expects(:uid).never + @provider.validuser?(10) + end + + it "should fail if it cannot convert an owner name to an integer" do + @provider.expects(:uid).with("foo").returns nil + @owner.should = %w{foo} + + lambda { @owner.insync?(10) }.should raise_error(Puppet::Error) + end + + it "should return false if the owners are not equal" do + @owner.should = [10] + @owner.should_not be_insync(20) + end + end + + describe "when changing the owner" do + before do + @owner.should = %w{one} + @owner.stubs(:path).returns "path" + @owner.stubs(:uid).returns 500 + end + + it "should chown the file if :links is set to :follow" do + @resource.expects(:[]).with(:links).returns :follow + File.expects(:chown) + + @owner.sync + end + + it "should lchown the file if :links is set to :manage" do + @resource.expects(:[]).with(:links).returns :manage + File.expects(:lchown) + + @owner.sync + end + + it "should use the first valid owner in its 'should' list" do + @owner.should = %w{one two three} + @provider.expects(:validuser?).with("one").returns nil + @provider.expects(:validuser?).with("two").returns 500 + @provider.expects(:validuser?).with("three").never + + File.expects(:chown).with(500, nil, "/my/file") + + @owner.sync + end + end +end diff --git a/spec/unit/type/file/owner_spec_spec.rb b/spec/unit/type/file/owner_spec_spec.rb deleted file mode 100755 index a077ccec2..000000000 --- a/spec/unit/type/file/owner_spec_spec.rb +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -property = Puppet::Type.type(:file).attrclass(:owner) - -describe property do - before do - # FIXME: many of these tests exercise the provider rather than `owner` - # and should be moved into provider tests. ~JW - @provider = Puppet::Type.type(:file).provider(:posix).new - @provider.stubs(:uid).with("one").returns(1) - - @resource = stub 'resource', :line => "foo", :file => "bar" - @resource.stubs(:[]).returns "foo" - @resource.stubs(:[]).with(:path).returns "/my/file" - @resource.stubs(:provider).returns @provider - - @owner = property.new :resource => @resource - end - - it "should have a method for testing whether an owner is valid" do - @provider.must respond_to(:validuser?) - end - - it "should return the found uid if an owner is valid" do - @provider.expects(:uid).with("foo").returns 500 - @provider.validuser?("foo").should == 500 - end - - it "should return false if an owner is not valid" do - @provider.expects(:uid).with("foo").returns nil - @provider.validuser?("foo").should be_false - end - - describe "when retrieving the current value" do - it "should return :absent if the file cannot stat" do - @resource.expects(:stat).returns nil - - @owner.retrieve.should == :absent - end - - it "should get the uid from the stat instance from the file" do - stat = stub 'stat', :ftype => "foo" - @resource.expects(:stat).returns stat - stat.expects(:uid).returns 500 - - @owner.retrieve.should == 500 - end - - it "should warn and return :silly if the found value is higher than the maximum uid value" do - Puppet.settings.expects(:value).with(:maximum_uid).returns 500 - - stat = stub 'stat', :ftype => "foo" - @resource.expects(:stat).returns stat - stat.expects(:uid).returns 1000 - - @provider.expects(:warning) - @owner.retrieve.should == :silly - end - end - - describe "when determining if the file is in sync" do - describe "and not running as root" do - it "should warn once and return true" do - Puppet.features.expects(:root?).returns false - - @provider.expects(:warnonce) - - @owner.should = [10] - @owner.must be_insync(20) - end - end - - before do - Puppet.features.stubs(:root?).returns true - end - - it "should be in sync if 'should' is not provided" do - @owner.must be_insync(10) - end - - it "should directly compare the owner values if the desired owner is an integer" do - @owner.should = [10] - @owner.must be_insync(10) - end - - it "should treat numeric strings as integers" do - @owner.should = ["10"] - @owner.must be_insync(10) - end - - it "should convert the owner name to an integer if the desired owner is a string" do - @provider.expects(:uid).with("foo").returns 10 - @owner.should = %w{foo} - - @owner.must be_insync(10) - end - - it "should not validate that users exist when a user is specified as an integer" do - @provider.expects(:uid).never - @provider.validuser?(10) - end - - it "should fail if it cannot convert an owner name to an integer" do - @provider.expects(:uid).with("foo").returns nil - @owner.should = %w{foo} - - lambda { @owner.insync?(10) }.should raise_error(Puppet::Error) - end - - it "should return false if the owners are not equal" do - @owner.should = [10] - @owner.should_not be_insync(20) - end - end - - describe "when changing the owner" do - before do - @owner.should = %w{one} - @owner.stubs(:path).returns "path" - @owner.stubs(:uid).returns 500 - end - - it "should chown the file if :links is set to :follow" do - @resource.expects(:[]).with(:links).returns :follow - File.expects(:chown) - - @owner.sync - end - - it "should lchown the file if :links is set to :manage" do - @resource.expects(:[]).with(:links).returns :manage - File.expects(:lchown) - - @owner.sync - end - - it "should use the first valid owner in its 'should' list" do - @owner.should = %w{one two three} - @provider.expects(:validuser?).with("one").returns nil - @provider.expects(:validuser?).with("two").returns 500 - @provider.expects(:validuser?).with("three").never - - File.expects(:chown).with(500, nil, "/my/file") - - @owner.sync - end - end -end diff --git a/spec/unit/type/file/selinux_spec.rb b/spec/unit/type/file/selinux_spec.rb new file mode 100644 index 000000000..af92a76d2 --- /dev/null +++ b/spec/unit/type/file/selinux_spec.rb @@ -0,0 +1,83 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + + +[:seluser, :selrole, :seltype, :selrange].each do |param| + property = Puppet::Type.type(:file).attrclass(param) + describe property do + before do + @resource = Puppet::Type.type(:file).new :path => "/my/file" + @sel = property.new :resource => @resource + end + + it "retrieve on #{param} should return :absent if the file isn't statable" do + @resource.expects(:stat).returns nil + @sel.retrieve.should == :absent + end + + it "should retrieve nil for #{param} if there is no SELinux support" do + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + @sel.expects(:get_selinux_current_context).with("/my/file").returns nil + @sel.retrieve.should be_nil + end + + it "should retrieve #{param} if a SELinux context is found with a range" do + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + @sel.expects(:get_selinux_current_context).with("/my/file").returns "user_u:role_r:type_t:s0" + expectedresult = case param + when :seluser; "user_u" + when :selrole; "role_r" + when :seltype; "type_t" + when :selrange; "s0" + end + @sel.retrieve.should == expectedresult + end + + it "should retrieve #{param} if a SELinux context is found without a range" do + stat = stub 'stat', :ftype => "foo" + @resource.expects(:stat).returns stat + @sel.expects(:get_selinux_current_context).with("/my/file").returns "user_u:role_r:type_t" + expectedresult = case param + when :seluser; "user_u" + when :selrole; "role_r" + when :seltype; "type_t" + when :selrange; nil + end + @sel.retrieve.should == expectedresult + end + + it "should handle no default gracefully" do + @sel.expects(:get_selinux_default_context).with("/my/file").returns nil + @sel.default.must be_nil + end + + it "should be able to detect matchpathcon defaults" do + @sel.stubs(:debug) + @sel.expects(:get_selinux_default_context).with("/my/file").returns "user_u:role_r:type_t:s0" + expectedresult = case param + when :seluser; "user_u" + when :selrole; "role_r" + when :seltype; "type_t" + when :selrange; "s0" + end + @sel.default.must == expectedresult + end + + it "should be able to set a new context" do + stat = stub 'stat', :ftype => "foo" + @sel.should = %w{newone} + @sel.expects(:set_selinux_context).with("/my/file", ["newone"], param) + @sel.sync + end + + it "should do nothing for insync? if no SELinux support" do + @sel.should = %{newcontext} + @sel.expects(:selinux_support?).returns false + @sel.insync?("oldcontext").should == true + end + end +end + diff --git a/spec/unit/type/file/selinux_spec_spec.rb b/spec/unit/type/file/selinux_spec_spec.rb deleted file mode 100644 index af92a76d2..000000000 --- a/spec/unit/type/file/selinux_spec_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - - -[:seluser, :selrole, :seltype, :selrange].each do |param| - property = Puppet::Type.type(:file).attrclass(param) - describe property do - before do - @resource = Puppet::Type.type(:file).new :path => "/my/file" - @sel = property.new :resource => @resource - end - - it "retrieve on #{param} should return :absent if the file isn't statable" do - @resource.expects(:stat).returns nil - @sel.retrieve.should == :absent - end - - it "should retrieve nil for #{param} if there is no SELinux support" do - stat = stub 'stat', :ftype => "foo" - @resource.expects(:stat).returns stat - @sel.expects(:get_selinux_current_context).with("/my/file").returns nil - @sel.retrieve.should be_nil - end - - it "should retrieve #{param} if a SELinux context is found with a range" do - stat = stub 'stat', :ftype => "foo" - @resource.expects(:stat).returns stat - @sel.expects(:get_selinux_current_context).with("/my/file").returns "user_u:role_r:type_t:s0" - expectedresult = case param - when :seluser; "user_u" - when :selrole; "role_r" - when :seltype; "type_t" - when :selrange; "s0" - end - @sel.retrieve.should == expectedresult - end - - it "should retrieve #{param} if a SELinux context is found without a range" do - stat = stub 'stat', :ftype => "foo" - @resource.expects(:stat).returns stat - @sel.expects(:get_selinux_current_context).with("/my/file").returns "user_u:role_r:type_t" - expectedresult = case param - when :seluser; "user_u" - when :selrole; "role_r" - when :seltype; "type_t" - when :selrange; nil - end - @sel.retrieve.should == expectedresult - end - - it "should handle no default gracefully" do - @sel.expects(:get_selinux_default_context).with("/my/file").returns nil - @sel.default.must be_nil - end - - it "should be able to detect matchpathcon defaults" do - @sel.stubs(:debug) - @sel.expects(:get_selinux_default_context).with("/my/file").returns "user_u:role_r:type_t:s0" - expectedresult = case param - when :seluser; "user_u" - when :selrole; "role_r" - when :seltype; "type_t" - when :selrange; "s0" - end - @sel.default.must == expectedresult - end - - it "should be able to set a new context" do - stat = stub 'stat', :ftype => "foo" - @sel.should = %w{newone} - @sel.expects(:set_selinux_context).with("/my/file", ["newone"], param) - @sel.sync - end - - it "should do nothing for insync? if no SELinux support" do - @sel.should = %{newcontext} - @sel.expects(:selinux_support?).returns false - @sel.insync?("oldcontext").should == true - end - end -end - diff --git a/spec/unit/type/file/source_spec.rb b/spec/unit/type/file/source_spec.rb new file mode 100755 index 000000000..536cb63b0 --- /dev/null +++ b/spec/unit/type/file/source_spec.rb @@ -0,0 +1,272 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +source = Puppet::Type.type(:file).attrclass(:source) +describe Puppet::Type.type(:file).attrclass(:source) do + before do + # Wow that's a messy interface to the resource. + @resource = stub 'resource', :[]= => nil, :property => nil, :catalog => stub("catalog", :dependent_data_expired? => false) + end + + it "should be a subclass of Parameter" do + source.superclass.must == Puppet::Parameter + end + + describe "when initializing" do + it "should fail if the set values are not URLs" do + s = source.new(:resource => @resource) + URI.expects(:parse).with('foo').raises RuntimeError + + lambda { s.value = %w{foo} }.must raise_error(Puppet::Error) + end + + it "should fail if the URI is not a local file, file URI, or puppet URI" do + s = source.new(:resource => @resource) + + lambda { s.value = %w{http://foo/bar} }.must raise_error(Puppet::Error) + end + end + + it "should have a method for retrieving its metadata" do + source.new(:resource => @resource).must respond_to(:metadata) + end + + it "should have a method for setting its metadata" do + source.new(:resource => @resource).must respond_to(:metadata=) + end + + describe "when returning the metadata" do + before do + @metadata = stub 'metadata', :source= => nil + end + + it "should return already-available metadata" do + @source = source.new(:resource => @resource) + @source.metadata = "foo" + @source.metadata.should == "foo" + end + + it "should return nil if no @should value is set and no metadata is available" do + @source = source.new(:resource => @resource) + @source.metadata.should be_nil + end + + it "should collect its metadata using the Metadata class if it is not already set" do + @source = source.new(:resource => @resource, :value => "/foo/bar") + Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns @metadata + @source.metadata + end + + it "should use the metadata from the first found source" do + metadata = stub 'metadata', :source= => nil + @source = source.new(:resource => @resource, :value => ["/foo/bar", "/fee/booz"]) + Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns nil + Puppet::FileServing::Metadata.expects(:find).with("/fee/booz").returns metadata + @source.metadata.should equal(metadata) + end + + it "should store the found source as the metadata's source" do + metadata = mock 'metadata' + @source = source.new(:resource => @resource, :value => "/foo/bar") + Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns metadata + + metadata.expects(:source=).with("/foo/bar") + @source.metadata + end + + it "should fail intelligently if an exception is encountered while querying for metadata" do + @source = source.new(:resource => @resource, :value => "/foo/bar") + Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").raises RuntimeError + + @source.expects(:fail).raises ArgumentError + lambda { @source.metadata }.should raise_error(ArgumentError) + end + + it "should fail if no specified sources can be found" do + @source = source.new(:resource => @resource, :value => "/foo/bar") + Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns nil + + @source.expects(:fail).raises RuntimeError + + lambda { @source.metadata }.should raise_error(RuntimeError) + end + + it "should expire the metadata appropriately" do + expirer = stub 'expired', :dependent_data_expired? => true + + metadata = stub 'metadata', :source= => nil + Puppet::FileServing::Metadata.expects(:find).with("/fee/booz").returns metadata + + @source = source.new(:resource => @resource, :value => ["/fee/booz"]) + @source.metadata = "foo" + + @source.stubs(:expirer).returns expirer + + @source.metadata.should_not == "foo" + end + end + + it "should have a method for setting the desired values on the resource" do + source.new(:resource => @resource).must respond_to(:copy_source_values) + end + + describe "when copying the source values" do + before do + + @resource = Puppet::Type.type(:file).new :path => "/foo/bar" + + @source = source.new(:resource => @resource) + @metadata = stub 'metadata', :owner => 100, :group => 200, :mode => 123, :checksum => "{md5}asdfasdf", :ftype => "file" + @source.stubs(:metadata).returns @metadata + end + + it "should fail if there is no metadata" do + @source.stubs(:metadata).returns nil + @source.expects(:devfail).raises ArgumentError + lambda { @source.copy_source_values }.should raise_error(ArgumentError) + end + + it "should set :ensure to the file type" do + @metadata.stubs(:ftype).returns "file" + + @source.copy_source_values + @resource[:ensure].must == :file + end + + it "should not set 'ensure' if it is already set to 'absent'" do + @metadata.stubs(:ftype).returns "file" + + @resource[:ensure] = :absent + @source.copy_source_values + @resource[:ensure].must == :absent + end + + describe "and the source is a file" do + before do + @metadata.stubs(:ftype).returns "file" + end + + it "should copy the metadata's owner, group, checksum, and mode to the resource if they are not set on the resource" do + Puppet.features.expects(:root?).returns true + + @source.copy_source_values + + @resource[:owner].must == 100 + @resource[:group].must == 200 + @resource[:mode].must == 123 + + # Metadata calls it checksum, we call it content. + @resource[:content].must == @metadata.checksum + end + + it "should not copy the metadata's owner to the resource if it is already set" do + @resource[:owner] = 1 + @resource[:group] = 2 + @resource[:mode] = 3 + @resource[:content] = "foobar" + + @source.copy_source_values + + @resource[:owner].must == 1 + @resource[:group].must == 2 + @resource[:mode].must == 3 + @resource[:content].should_not == @metadata.checksum + end + + describe "and puppet is not running as root" do + it "should not try to set the owner" do + Puppet.features.expects(:root?).returns false + + @source.copy_source_values + @resource[:owner].should be_nil + end + end + end + + describe "and the source is a link" do + it "should set the target to the link destination" do + @metadata.stubs(:ftype).returns "link" + @resource.stubs(:[]) + @resource.stubs(:[]=) + + @metadata.expects(:destination).returns "/path/to/symlink" + + @resource.expects(:[]=).with(:target, "/path/to/symlink") + @source.copy_source_values + end + end + end + + it "should have a local? method" do + source.new(:resource => @resource).must be_respond_to(:local?) + end + + context "when accessing source properties" do + before(:each) do + @source = source.new(:resource => @resource) + @metadata = stub_everything + @source.stubs(:metadata).returns(@metadata) + end + + describe "for local sources" do + before(:each) do + @metadata.stubs(:ftype).returns "file" + @metadata.stubs(:source).returns("file:///path/to/source") + end + + it "should be local" do + @source.must be_local + end + + it "should be local if there is no scheme" do + @metadata.stubs(:source).returns("/path/to/source") + @source.must be_local + end + + it "should be able to return the metadata source full path" do + @source.full_path.should == "/path/to/source" + end + end + + describe "for remote sources" do + before(:each) do + @metadata.stubs(:ftype).returns "file" + @metadata.stubs(:source).returns("puppet://server:8192/path/to/source") + end + + it "should not be local" do + @source.should_not be_local + end + + it "should be able to return the metadata source full path" do + @source.full_path.should == "/path/to/source" + end + + it "should be able to return the source server" do + @source.server.should == "server" + end + + it "should be able to return the source port" do + @source.port.should == 8192 + end + + describe "which don't specify server or port" do + before(:each) do + @metadata.stubs(:source).returns("puppet:///path/to/source") + end + + it "should return the default source server" do + Puppet.settings.expects(:[]).with(:server).returns("myserver") + @source.server.should == "myserver" + end + + it "should return the default source port" do + Puppet.settings.expects(:[]).with(:masterport).returns(1234) + @source.port.should == 1234 + end + end + end + end + +end diff --git a/spec/unit/type/file/source_spec_spec.rb b/spec/unit/type/file/source_spec_spec.rb deleted file mode 100755 index 536cb63b0..000000000 --- a/spec/unit/type/file/source_spec_spec.rb +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -source = Puppet::Type.type(:file).attrclass(:source) -describe Puppet::Type.type(:file).attrclass(:source) do - before do - # Wow that's a messy interface to the resource. - @resource = stub 'resource', :[]= => nil, :property => nil, :catalog => stub("catalog", :dependent_data_expired? => false) - end - - it "should be a subclass of Parameter" do - source.superclass.must == Puppet::Parameter - end - - describe "when initializing" do - it "should fail if the set values are not URLs" do - s = source.new(:resource => @resource) - URI.expects(:parse).with('foo').raises RuntimeError - - lambda { s.value = %w{foo} }.must raise_error(Puppet::Error) - end - - it "should fail if the URI is not a local file, file URI, or puppet URI" do - s = source.new(:resource => @resource) - - lambda { s.value = %w{http://foo/bar} }.must raise_error(Puppet::Error) - end - end - - it "should have a method for retrieving its metadata" do - source.new(:resource => @resource).must respond_to(:metadata) - end - - it "should have a method for setting its metadata" do - source.new(:resource => @resource).must respond_to(:metadata=) - end - - describe "when returning the metadata" do - before do - @metadata = stub 'metadata', :source= => nil - end - - it "should return already-available metadata" do - @source = source.new(:resource => @resource) - @source.metadata = "foo" - @source.metadata.should == "foo" - end - - it "should return nil if no @should value is set and no metadata is available" do - @source = source.new(:resource => @resource) - @source.metadata.should be_nil - end - - it "should collect its metadata using the Metadata class if it is not already set" do - @source = source.new(:resource => @resource, :value => "/foo/bar") - Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns @metadata - @source.metadata - end - - it "should use the metadata from the first found source" do - metadata = stub 'metadata', :source= => nil - @source = source.new(:resource => @resource, :value => ["/foo/bar", "/fee/booz"]) - Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns nil - Puppet::FileServing::Metadata.expects(:find).with("/fee/booz").returns metadata - @source.metadata.should equal(metadata) - end - - it "should store the found source as the metadata's source" do - metadata = mock 'metadata' - @source = source.new(:resource => @resource, :value => "/foo/bar") - Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns metadata - - metadata.expects(:source=).with("/foo/bar") - @source.metadata - end - - it "should fail intelligently if an exception is encountered while querying for metadata" do - @source = source.new(:resource => @resource, :value => "/foo/bar") - Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").raises RuntimeError - - @source.expects(:fail).raises ArgumentError - lambda { @source.metadata }.should raise_error(ArgumentError) - end - - it "should fail if no specified sources can be found" do - @source = source.new(:resource => @resource, :value => "/foo/bar") - Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns nil - - @source.expects(:fail).raises RuntimeError - - lambda { @source.metadata }.should raise_error(RuntimeError) - end - - it "should expire the metadata appropriately" do - expirer = stub 'expired', :dependent_data_expired? => true - - metadata = stub 'metadata', :source= => nil - Puppet::FileServing::Metadata.expects(:find).with("/fee/booz").returns metadata - - @source = source.new(:resource => @resource, :value => ["/fee/booz"]) - @source.metadata = "foo" - - @source.stubs(:expirer).returns expirer - - @source.metadata.should_not == "foo" - end - end - - it "should have a method for setting the desired values on the resource" do - source.new(:resource => @resource).must respond_to(:copy_source_values) - end - - describe "when copying the source values" do - before do - - @resource = Puppet::Type.type(:file).new :path => "/foo/bar" - - @source = source.new(:resource => @resource) - @metadata = stub 'metadata', :owner => 100, :group => 200, :mode => 123, :checksum => "{md5}asdfasdf", :ftype => "file" - @source.stubs(:metadata).returns @metadata - end - - it "should fail if there is no metadata" do - @source.stubs(:metadata).returns nil - @source.expects(:devfail).raises ArgumentError - lambda { @source.copy_source_values }.should raise_error(ArgumentError) - end - - it "should set :ensure to the file type" do - @metadata.stubs(:ftype).returns "file" - - @source.copy_source_values - @resource[:ensure].must == :file - end - - it "should not set 'ensure' if it is already set to 'absent'" do - @metadata.stubs(:ftype).returns "file" - - @resource[:ensure] = :absent - @source.copy_source_values - @resource[:ensure].must == :absent - end - - describe "and the source is a file" do - before do - @metadata.stubs(:ftype).returns "file" - end - - it "should copy the metadata's owner, group, checksum, and mode to the resource if they are not set on the resource" do - Puppet.features.expects(:root?).returns true - - @source.copy_source_values - - @resource[:owner].must == 100 - @resource[:group].must == 200 - @resource[:mode].must == 123 - - # Metadata calls it checksum, we call it content. - @resource[:content].must == @metadata.checksum - end - - it "should not copy the metadata's owner to the resource if it is already set" do - @resource[:owner] = 1 - @resource[:group] = 2 - @resource[:mode] = 3 - @resource[:content] = "foobar" - - @source.copy_source_values - - @resource[:owner].must == 1 - @resource[:group].must == 2 - @resource[:mode].must == 3 - @resource[:content].should_not == @metadata.checksum - end - - describe "and puppet is not running as root" do - it "should not try to set the owner" do - Puppet.features.expects(:root?).returns false - - @source.copy_source_values - @resource[:owner].should be_nil - end - end - end - - describe "and the source is a link" do - it "should set the target to the link destination" do - @metadata.stubs(:ftype).returns "link" - @resource.stubs(:[]) - @resource.stubs(:[]=) - - @metadata.expects(:destination).returns "/path/to/symlink" - - @resource.expects(:[]=).with(:target, "/path/to/symlink") - @source.copy_source_values - end - end - end - - it "should have a local? method" do - source.new(:resource => @resource).must be_respond_to(:local?) - end - - context "when accessing source properties" do - before(:each) do - @source = source.new(:resource => @resource) - @metadata = stub_everything - @source.stubs(:metadata).returns(@metadata) - end - - describe "for local sources" do - before(:each) do - @metadata.stubs(:ftype).returns "file" - @metadata.stubs(:source).returns("file:///path/to/source") - end - - it "should be local" do - @source.must be_local - end - - it "should be local if there is no scheme" do - @metadata.stubs(:source).returns("/path/to/source") - @source.must be_local - end - - it "should be able to return the metadata source full path" do - @source.full_path.should == "/path/to/source" - end - end - - describe "for remote sources" do - before(:each) do - @metadata.stubs(:ftype).returns "file" - @metadata.stubs(:source).returns("puppet://server:8192/path/to/source") - end - - it "should not be local" do - @source.should_not be_local - end - - it "should be able to return the metadata source full path" do - @source.full_path.should == "/path/to/source" - end - - it "should be able to return the source server" do - @source.server.should == "server" - end - - it "should be able to return the source port" do - @source.port.should == 8192 - end - - describe "which don't specify server or port" do - before(:each) do - @metadata.stubs(:source).returns("puppet:///path/to/source") - end - - it "should return the default source server" do - Puppet.settings.expects(:[]).with(:server).returns("myserver") - @source.server.should == "myserver" - end - - it "should return the default source port" do - Puppet.settings.expects(:[]).with(:masterport).returns(1234) - @source.port.should == 1234 - end - end - end - end - -end diff --git a/spec/unit/type/file_spec.rb b/spec/unit/type/file_spec.rb new file mode 100755 index 000000000..7014854c1 --- /dev/null +++ b/spec/unit/type/file_spec.rb @@ -0,0 +1,1053 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type.type(:file) do + before do + Puppet.settings.stubs(:use) + @real_posix = Puppet.features.posix? + Puppet.features.stubs("posix?").returns(true) + + @path = Tempfile.new("puppetspec") + pathname = @path.path + @path.close!() + @path = pathname + @file = Puppet::Type::File.new(:name => @path) + + @catalog = Puppet::Resource::Catalog.new + @file.catalog = @catalog + end + + describe "when determining if recursion is enabled" do + it "should default to recursion being disabled" do + @file.should_not be_recurse + end + [true, "true", 10, "inf", "remote"].each do |value| + it "should consider #{value} to enable recursion" do + @file[:recurse] = value + @file.must be_recurse + end + end + + [false, "false", 0].each do |value| + it "should consider #{value} to disable recursion" do + @file[:recurse] = value + @file.should_not be_recurse + end + end + end + + describe "#write" do + + it "should propagate failures encountered when renaming the temporary file" do + File.stubs(:open) + + File.expects(:rename).raises ArgumentError + file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") + + file.stubs(:validate_checksum?).returns(false) + + property = stub('content_property', :actual_content => "something", :length => "something".length) + file.stubs(:property).with(:content).returns(property) + + lambda { file.write(:content) }.should raise_error(Puppet::Error) + end + + it "should delegate writing to the content property" do + filehandle = stub_everything 'fh' + File.stubs(:open).yields(filehandle) + File.stubs(:rename) + property = stub('content_property', :actual_content => "something", :length => "something".length) + file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") + file.stubs(:validate_checksum?).returns(false) + file.stubs(:property).with(:content).returns(property) + + property.expects(:write).with(filehandle) + + file.write(:content) + end + + describe "when validating the checksum" do + before { @file.stubs(:validate_checksum?).returns(true) } + + it "should fail if the checksum parameter and content checksums do not match" do + checksum = stub('checksum_parameter', :sum => 'checksum_b') + @file.stubs(:parameter).with(:checksum).returns(checksum) + + property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') + @file.stubs(:property).with(:content).returns(property) + + lambda { @file.write :NOTUSED }.should raise_error(Puppet::Error) + end + end + + describe "when not validating the checksum" do + before { @file.stubs(:validate_checksum?).returns(false) } + + it "should not fail if the checksum property and content checksums do not match" do + checksum = stub('checksum_parameter', :sum => 'checksum_b') + @file.stubs(:parameter).with(:checksum).returns(checksum) + + property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') + @file.stubs(:property).with(:content).returns(property) + + lambda { @file.write :NOTUSED }.should_not raise_error(Puppet::Error) + end + + end + end + + it "should have a method for determining if the file is present" do + @file.must respond_to(:exist?) + end + + it "should be considered existent if it can be stat'ed" do + @file.expects(:stat).returns mock('stat') + @file.must be_exist + end + + it "should be considered nonexistent if it can not be stat'ed" do + @file.expects(:stat).returns nil + @file.must_not be_exist + end + + it "should have a method for determining if the file should be a normal file" do + @file.must respond_to(:should_be_file?) + end + + it "should be a file if :ensure is set to :file" do + @file[:ensure] = :file + @file.must be_should_be_file + end + + it "should be a file if :ensure is set to :present and the file exists as a normal file" do + @file.stubs(:stat).returns(mock('stat', :ftype => "file")) + @file[:ensure] = :present + @file.must be_should_be_file + end + + it "should not be a file if :ensure is set to something other than :file" do + @file[:ensure] = :directory + @file.must_not be_should_be_file + end + + it "should not be a file if :ensure is set to :present and the file exists but is not a normal file" do + @file.stubs(:stat).returns(mock('stat', :ftype => "directory")) + @file[:ensure] = :present + @file.must_not be_should_be_file + end + + it "should be a file if :ensure is not set and :content is" do + @file[:content] = "foo" + @file.must be_should_be_file + end + + it "should be a file if neither :ensure nor :content is set but the file exists as a normal file" do + @file.stubs(:stat).returns(mock("stat", :ftype => "file")) + @file.must be_should_be_file + end + + it "should not be a file if neither :ensure nor :content is set but the file exists but not as a normal file" do + @file.stubs(:stat).returns(mock("stat", :ftype => "directory")) + @file.must_not be_should_be_file + end + + describe "when using POSIX filenames" do + describe "on POSIX systems" do + before do + Puppet.features.stubs(:posix?).returns(true) + Puppet.features.stubs(:win32?).returns(false) + end + + it "should autorequire its parent directory" do + file = Puppet::Type::File.new(:path => "/foo/bar") + dir = Puppet::Type::File.new(:path => "/foo") + @catalog.add_resource file + @catalog.add_resource dir + reqs = file.autorequire + reqs[0].source.must == dir + reqs[0].target.must == file + end + + it "should not autorequire its parent dir if its parent dir is itself" do + file = Puppet::Type::File.new(:path => "/") + @catalog.add_resource file + file.autorequire.should be_empty + end + + it "should remove trailing slashes" do + file = Puppet::Type::File.new(:path => "/foo/bar/baz/") + file[:path].should == "/foo/bar/baz" + end + + it "should remove double slashes" do + file = Puppet::Type::File.new(:path => "/foo/bar//baz") + file[:path].should == "/foo/bar/baz" + end + + it "should remove trailing double slashes" do + file = Puppet::Type::File.new(:path => "/foo/bar/baz//") + file[:path].should == "/foo/bar/baz" + end + + it "should leave a single slash alone" do + file = Puppet::Type::File.new(:path => "/") + file[:path].should == "/" + end + end + + describe "on Win32 systems" do + before do + Puppet.features.stubs(:posix?).returns(false) + Puppet.features.stubs(:win32?).returns(true) + end + + it "should refuse to work" do + lambda { Puppet::Type::File.new(:path => "/foo/bar") }.should raise_error(Puppet::Error) + end + end + end + + describe "when using Win32 filenames" do + confine "Only works on Win32" => Puppet.features.win32? + describe "on Win32 systems" do + before do + Puppet.features.stubs(:posix?).returns(false) + Puppet.features.stubs(:win32?).returns(true) + end + + it "should autorequire its parent directory" do + file = Puppet::Type::File.new(:path => "X:/foo/bar") + dir = Puppet::Type::File.new(:path => "X:/foo") + @catalog.add_resource file + @catalog.add_resource dir + reqs = file.autorequire + reqs[0].source.must == dir + reqs[0].target.must == file + end + + it "should not autorequire its parent dir if its parent dir is itself" do + file = Puppet::Type::File.new(:path => "X:/") + @catalog.add_resource file + file.autorequire.should be_empty + end + + it "should remove trailing slashes" do + file = Puppet::Type::File.new(:path => "X:/foo/bar/baz/") + file[:path].should == "X:/foo/bar/baz" + end + + it "should remove double slashes" do + file = Puppet::Type::File.new(:path => "X:/foo/bar//baz") + file[:path].should == "X:/foo/bar/baz" + end + + it "should remove trailing double slashes" do + file = Puppet::Type::File.new(:path => "X:/foo/bar/baz//") + file[:path].should == "X:/foo/bar/baz" + end + + it "should leave a drive letter with a slash alone" do + file = Puppet::Type::File.new(:path => "X:/") + file[:path].should == "X:/" + end + + it "should add a slash to a drive letter" do + file = Puppet::Type::File.new(:path => "X:") + file[:path].should == "X:/" + end + end + + describe "on POSIX systems" do + before do + Puppet.features.stubs(:posix?).returns(true) + Puppet.features.stubs(:win32?).returns(false) + end + + it "should refuse to work" do + lambda { Puppet::Type::File.new(:path => "X:/foo/bar") }.should raise_error(Puppet::Error) + end + end + end + + describe "when using UNC filenames" do + describe "on Win32 systems" do + confine "Only works on Win32" => Puppet.features.win32? + before do + Puppet.features.stubs(:posix?).returns(false) + Puppet.features.stubs(:win32?).returns(true) + end + + it "should autorequire its parent directory" do + file = Puppet::Type::File.new(:path => "//server/foo/bar") + dir = Puppet::Type::File.new(:path => "//server/foo") + @catalog.add_resource file + @catalog.add_resource dir + reqs = file.autorequire + reqs[0].source.must == dir + reqs[0].target.must == file + end + + it "should not autorequire its parent dir if its parent dir is itself" do + file = Puppet::Type::File.new(:path => "//server/foo") + @catalog.add_resource file + puts file.autorequire + file.autorequire.should be_empty + end + + it "should remove trailing slashes" do + file = Puppet::Type::File.new(:path => "//server/foo/bar/baz/") + file[:path].should == "//server/foo/bar/baz" + end + + it "should remove double slashes" do + file = Puppet::Type::File.new(:path => "//server/foo/bar//baz") + file[:path].should == "//server/foo/bar/baz" + end + + it "should remove trailing double slashes" do + file = Puppet::Type::File.new(:path => "//server/foo/bar/baz//") + file[:path].should == "//server/foo/bar/baz" + end + + it "should remove a trailing slash from a sharename" do + file = Puppet::Type::File.new(:path => "//server/foo/") + file[:path].should == "//server/foo" + end + + it "should not modify a sharename" do + file = Puppet::Type::File.new(:path => "//server/foo") + file[:path].should == "//server/foo" + end + end + + describe "on POSIX systems" do + before do + Puppet.features.stubs(:posix?).returns(true) + Puppet.features.stubs(:win32?).returns(false) + end + + it "should refuse to work" do + lambda { Puppet::Type::File.new(:path => "X:/foo/bar") }.should raise_error(Puppet::Error) + end + end + end + + describe "when initializing" do + it "should set a desired 'ensure' value if none is set and 'content' is set" do + file = Puppet::Type::File.new(:name => "/my/file", :content => "/foo/bar") + file[:ensure].should == :file + end + + it "should set a desired 'ensure' value if none is set and 'target' is set" do + file = Puppet::Type::File.new(:name => "/my/file", :target => "/foo/bar") + file[:ensure].should == :symlink + end + end + + describe "when validating attributes" do + %w{path checksum backup recurse recurselimit source replace force ignore links purge sourceselect}.each do |attr| + it "should have a '#{attr}' parameter" do + Puppet::Type.type(:file).attrtype(attr.intern).should == :param + end + end + + %w{content target ensure owner group mode type}.each do |attr| + it "should have a '#{attr}' property" do + Puppet::Type.type(:file).attrtype(attr.intern).should == :property + end + end + + it "should have its 'path' attribute set as its namevar" do + Puppet::Type.type(:file).key_attributes.should == [:path] + end + end + + describe "when managing links" do + require 'puppettest/support/assertions' + include PuppetTest + require 'tempfile' + + if @real_posix + describe "on POSIX systems" do + before do + @basedir = tempfile + Dir.mkdir(@basedir) + @file = File.join(@basedir, "file") + @link = File.join(@basedir, "link") + + File.open(@file, "w", 0644) { |f| f.puts "yayness"; f.flush } + File.symlink(@file, @link) + + @resource = Puppet::Type.type(:file).new( + :path => @link, + :mode => "755" + ) + @catalog.add_resource @resource + end + + after do + remove_tmp_files + end + + it "should default to managing the link" do + @catalog.apply + # I convert them to strings so they display correctly if there's an error. + ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0644 + end + + it "should be able to follow links" do + @resource[:links] = :follow + @catalog.apply + + ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0755 + end + end + else # @real_posix + # should recode tests using expectations instead of using the filesystem + end + + describe "on Win32 systems" do + before do + Puppet.features.stubs(:posix?).returns(false) + Puppet.features.stubs(:win32?).returns(true) + end + + it "should refuse to work with links" + end + end + + it "should be able to retrieve a stat instance for the file it is managing" do + Puppet::Type.type(:file).new(:path => "/foo/bar", :source => "/bar/foo").should respond_to(:stat) + end + + describe "when stat'ing its file" do + before do + @resource = Puppet::Type.type(:file).new(:path => "/foo/bar") + @resource[:links] = :manage # so we always use :lstat + end + + it "should use :stat if it is following links" do + @resource[:links] = :follow + File.expects(:stat) + + @resource.stat + end + + it "should use :lstat if is it not following links" do + @resource[:links] = :manage + File.expects(:lstat) + + @resource.stat + end + + it "should stat the path of the file" do + File.expects(:lstat).with("/foo/bar") + + @resource.stat + end + + # This only happens in testing. + it "should return nil if the stat does not exist" do + File.expects(:lstat).returns nil + + @resource.stat.should be_nil + end + + it "should return nil if the file does not exist" do + File.expects(:lstat).raises(Errno::ENOENT) + + @resource.stat.should be_nil + end + + it "should return nil if the file cannot be stat'ed" do + File.expects(:lstat).raises(Errno::EACCES) + + @resource.stat.should be_nil + end + + it "should return the stat instance" do + File.expects(:lstat).returns "mystat" + + @resource.stat.should == "mystat" + end + + it "should cache the stat instance if it has a catalog and is applying" do + stat = mock 'stat' + File.expects(:lstat).returns stat + + catalog = Puppet::Resource::Catalog.new + @resource.catalog = catalog + + catalog.stubs(:applying?).returns true + + @resource.stat.should equal(@resource.stat) + end + end + + describe "when flushing" do + it "should flush all properties that respond to :flush" do + @resource = Puppet::Type.type(:file).new(:path => "/foo/bar", :source => "/bar/foo") + @resource.parameter(:source).expects(:flush) + @resource.flush + end + + it "should reset its stat reference" do + @resource = Puppet::Type.type(:file).new(:path => "/foo/bar") + File.expects(:lstat).times(2).returns("stat1").then.returns("stat2") + @resource.stat.should == "stat1" + @resource.flush + @resource.stat.should == "stat2" + end + end + + it "should have a method for performing recursion" do + @file.must respond_to(:perform_recursion) + end + + describe "when executing a recursive search" do + it "should use Metadata to do its recursion" do + Puppet::FileServing::Metadata.expects(:search) + @file.perform_recursion(@file[:path]) + end + + it "should use the provided path as the key to the search" do + Puppet::FileServing::Metadata.expects(:search).with { |key, options| key == "/foo" } + @file.perform_recursion("/foo") + end + + it "should return the results of the metadata search" do + Puppet::FileServing::Metadata.expects(:search).returns "foobar" + @file.perform_recursion(@file[:path]).should == "foobar" + end + + it "should pass its recursion value to the search" do + @file[:recurse] = true + Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:recurse] == true } + @file.perform_recursion(@file[:path]) + end + + it "should pass true if recursion is remote" do + @file[:recurse] = :remote + Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:recurse] == true } + @file.perform_recursion(@file[:path]) + end + + it "should pass its recursion limit value to the search" do + @file[:recurselimit] = 10 + Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:recurselimit] == 10 } + @file.perform_recursion(@file[:path]) + end + + it "should configure the search to ignore or manage links" do + @file[:links] = :manage + Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:links] == :manage } + @file.perform_recursion(@file[:path]) + end + + it "should pass its 'ignore' setting to the search if it has one" do + @file[:ignore] = %w{.svn CVS} + Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:ignore] == %w{.svn CVS} } + @file.perform_recursion(@file[:path]) + end + end + + it "should have a method for performing local recursion" do + @file.must respond_to(:recurse_local) + end + + describe "when doing local recursion" do + before do + @metadata = stub 'metadata', :relative_path => "my/file" + end + + it "should pass its path to the :perform_recursion method" do + @file.expects(:perform_recursion).with(@file[:path]).returns [@metadata] + @file.stubs(:newchild) + @file.recurse_local + end + + it "should return an empty hash if the recursion returns nothing" do + @file.expects(:perform_recursion).returns nil + @file.recurse_local.should == {} + end + + it "should create a new child resource with each generated metadata instance's relative path" do + @file.expects(:perform_recursion).returns [@metadata] + @file.expects(:newchild).with(@metadata.relative_path).returns "fiebar" + @file.recurse_local + end + + it "should not create a new child resource for the '.' directory" do + @metadata.stubs(:relative_path).returns "." + + @file.expects(:perform_recursion).returns [@metadata] + @file.expects(:newchild).never + @file.recurse_local + end + + it "should return a hash of the created resources with the relative paths as the hash keys" do + @file.expects(:perform_recursion).returns [@metadata] + @file.expects(:newchild).with("my/file").returns "fiebar" + @file.recurse_local.should == {"my/file" => "fiebar"} + end + + it "should set checksum_type to none if this file checksum is none" do + @file[:checksum] = :none + Puppet::FileServing::Metadata.expects(:search).with { |path,params| params[:checksum_type] == :none }.returns [@metadata] + @file.expects(:newchild).with("my/file").returns "fiebar" + @file.recurse_local + end + end + + it "should have a method for performing link recursion" do + @file.must respond_to(:recurse_link) + end + + describe "when doing link recursion" do + before do + @first = stub 'first', :relative_path => "first", :full_path => "/my/first", :ftype => "directory" + @second = stub 'second', :relative_path => "second", :full_path => "/my/second", :ftype => "file" + + @resource = stub 'file', :[]= => nil + end + + it "should pass its target to the :perform_recursion method" do + @file[:target] = "mylinks" + @file.expects(:perform_recursion).with("mylinks").returns [@first] + @file.stubs(:newchild).returns @resource + @file.recurse_link({}) + end + + it "should ignore the recursively-found '.' file and configure the top-level file to create a directory" do + @first.stubs(:relative_path).returns "." + @file[:target] = "mylinks" + @file.expects(:perform_recursion).with("mylinks").returns [@first] + @file.stubs(:newchild).never + @file.expects(:[]=).with(:ensure, :directory) + @file.recurse_link({}) + end + + it "should create a new child resource for each generated metadata instance's relative path that doesn't already exist in the children hash" do + @file.expects(:perform_recursion).returns [@first, @second] + @file.expects(:newchild).with(@first.relative_path).returns @resource + @file.recurse_link("second" => @resource) + end + + it "should not create a new child resource for paths that already exist in the children hash" do + @file.expects(:perform_recursion).returns [@first] + @file.expects(:newchild).never + @file.recurse_link("first" => @resource) + end + + it "should set the target to the full path of discovered file and set :ensure to :link if the file is not a directory" do + file = stub 'file' + file.expects(:[]=).with(:target, "/my/second") + file.expects(:[]=).with(:ensure, :link) + + @file.stubs(:perform_recursion).returns [@first, @second] + @file.recurse_link("first" => @resource, "second" => file) + end + + it "should :ensure to :directory if the file is a directory" do + file = stub 'file' + file.expects(:[]=).with(:ensure, :directory) + + @file.stubs(:perform_recursion).returns [@first, @second] + @file.recurse_link("first" => file, "second" => @resource) + end + + it "should return a hash with both created and existing resources with the relative paths as the hash keys" do + file = stub 'file', :[]= => nil + + @file.expects(:perform_recursion).returns [@first, @second] + @file.stubs(:newchild).returns file + @file.recurse_link("second" => @resource).should == {"second" => @resource, "first" => file} + end + end + + it "should have a method for performing remote recursion" do + @file.must respond_to(:recurse_remote) + end + + describe "when doing remote recursion" do + before do + @file[:source] = "puppet://foo/bar" + + @first = Puppet::FileServing::Metadata.new("/my", :relative_path => "first") + @second = Puppet::FileServing::Metadata.new("/my", :relative_path => "second") + @first.stubs(:ftype).returns "directory" + @second.stubs(:ftype).returns "directory" + + @parameter = stub 'property', :metadata= => nil + @resource = stub 'file', :[]= => nil, :parameter => @parameter + end + + it "should pass its source to the :perform_recursion method" do + data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar") + @file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] + @file.stubs(:newchild).returns @resource + @file.recurse_remote({}) + end + + it "should not recurse when the remote file is not a directory" do + data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => ".") + data.stubs(:ftype).returns "file" + @file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] + @file.expects(:newchild).never + @file.recurse_remote({}) + end + + it "should set the source of each returned file to the searched-for URI plus the found relative path" do + @first.expects(:source=).with File.join("puppet://foo/bar", @first.relative_path) + @file.expects(:perform_recursion).returns [@first] + @file.stubs(:newchild).returns @resource + @file.recurse_remote({}) + end + + it "should create a new resource for any relative file paths that do not already have a resource" do + @file.stubs(:perform_recursion).returns [@first] + @file.expects(:newchild).with("first").returns @resource + @file.recurse_remote({}).should == {"first" => @resource} + end + + it "should not create a new resource for any relative file paths that do already have a resource" do + @file.stubs(:perform_recursion).returns [@first] + @file.expects(:newchild).never + @file.recurse_remote("first" => @resource) + end + + it "should set the source of each resource to the source of the metadata" do + @file.stubs(:perform_recursion).returns [@first] + @resource.stubs(:[]=) + @resource.expects(:[]=).with(:source, File.join("puppet://foo/bar", @first.relative_path)) + @file.recurse_remote("first" => @resource) + end + + # LAK:FIXME This is a bug, but I can't think of a fix for it. Fortunately it's already + # filed, and when it's fixed, we'll just fix the whole flow. + it "should set the checksum type to :md5 if the remote file is a file" do + @first.stubs(:ftype).returns "file" + @file.stubs(:perform_recursion).returns [@first] + @resource.stubs(:[]=) + @resource.expects(:[]=).with(:checksum, :md5) + @file.recurse_remote("first" => @resource) + end + + it "should store the metadata in the source property for each resource so the source does not have to requery the metadata" do + @file.stubs(:perform_recursion).returns [@first] + @resource.expects(:parameter).with(:source).returns @parameter + + @parameter.expects(:metadata=).with(@first) + + @file.recurse_remote("first" => @resource) + end + + it "should not create a new resource for the '.' file" do + @first.stubs(:relative_path).returns "." + @file.stubs(:perform_recursion).returns [@first] + + @file.expects(:newchild).never + + @file.recurse_remote({}) + end + + it "should store the metadata in the main file's source property if the relative path is '.'" do + @first.stubs(:relative_path).returns "." + @file.stubs(:perform_recursion).returns [@first] + + @file.parameter(:source).expects(:metadata=).with @first + + @file.recurse_remote("first" => @resource) + end + + describe "and multiple sources are provided" do + describe "and :sourceselect is set to :first" do + it "should create file instances for the results for the first source to return any values" do + data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar") + @file[:source] = %w{/one /two /three /four} + @file.expects(:perform_recursion).with("/one").returns nil + @file.expects(:perform_recursion).with("/two").returns [] + @file.expects(:perform_recursion).with("/three").returns [data] + @file.expects(:perform_recursion).with("/four").never + @file.expects(:newchild).with("foobar").returns @resource + @file.recurse_remote({}) + end + end + + describe "and :sourceselect is set to :all" do + before do + @file[:sourceselect] = :all + end + + it "should return every found file that is not in a previous source" do + klass = Puppet::FileServing::Metadata + @file[:source] = %w{/one /two /three /four} + @file.stubs(:newchild).returns @resource + + one = [klass.new("/one", :relative_path => "a")] + @file.expects(:perform_recursion).with("/one").returns one + @file.expects(:newchild).with("a").returns @resource + + two = [klass.new("/two", :relative_path => "a"), klass.new("/two", :relative_path => "b")] + @file.expects(:perform_recursion).with("/two").returns two + @file.expects(:newchild).with("b").returns @resource + + three = [klass.new("/three", :relative_path => "a"), klass.new("/three", :relative_path => "c")] + @file.expects(:perform_recursion).with("/three").returns three + @file.expects(:newchild).with("c").returns @resource + + @file.expects(:perform_recursion).with("/four").returns [] + + @file.recurse_remote({}) + end + end + end + end + + describe "when returning resources with :eval_generate" do + before do + @graph = stub 'graph', :add_edge => nil + @catalog.stubs(:relationship_graph).returns @graph + + @file.catalog = @catalog + @file[:recurse] = true + end + + it "should recurse if recursion is enabled" do + resource = stub('resource', :[] => "resource") + @file.expects(:recurse?).returns true + @file.expects(:recurse).returns [resource] + @file.eval_generate.should == [resource] + end + + it "should not recurse if recursion is disabled" do + @file.expects(:recurse?).returns false + @file.expects(:recurse).never + @file.eval_generate.should == [] + end + + it "should return each resource found through recursion" do + foo = stub 'foo', :[] => "/foo" + bar = stub 'bar', :[] => "/bar" + bar2 = stub 'bar2', :[] => "/bar" + + @file.expects(:recurse).returns [foo, bar] + + @file.eval_generate.should == [foo, bar] + end + end + + describe "when recursing" do + before do + @file[:recurse] = true + @metadata = Puppet::FileServing::Metadata + end + + describe "and a source is set" do + before { @file[:source] = "/my/source" } + + it "should pass the already-discovered resources to recurse_remote" do + @file.stubs(:recurse_local).returns(:foo => "bar") + @file.expects(:recurse_remote).with(:foo => "bar").returns [] + @file.recurse + end + end + + describe "and a target is set" do + before { @file[:target] = "/link/target" } + + it "should use recurse_link" do + @file.stubs(:recurse_local).returns(:foo => "bar") + @file.expects(:recurse_link).with(:foo => "bar").returns [] + @file.recurse + end + end + + it "should use recurse_local if recurse is not remote" do + @file.expects(:recurse_local).returns({}) + @file.recurse + end + + it "should not use recurse_local if recurse remote" do + @file[:recurse] = :remote + @file.expects(:recurse_local).never + @file.recurse + end + + it "should return the generated resources as an array sorted by file path" do + one = stub 'one', :[] => "/one" + two = stub 'two', :[] => "/one/two" + three = stub 'three', :[] => "/three" + @file.expects(:recurse_local).returns(:one => one, :two => two, :three => three) + @file.recurse.should == [one, two, three] + end + + describe "and purging is enabled" do + before do + @file[:purge] = true + end + + it "should configure each file to be removed" do + local = stub 'local' + local.stubs(:[]).with(:source).returns nil # Thus, a local file + local.stubs(:[]).with(:path).returns "foo" + @file.expects(:recurse_local).returns("local" => local) + local.expects(:[]=).with(:ensure, :absent) + + @file.recurse + end + + it "should not remove files that exist in the remote repository" do + @file["source"] = "/my/file" + @file.expects(:recurse_local).returns({}) + + remote = stub 'remote' + remote.stubs(:[]).with(:source).returns "/whatever" # Thus, a remote file + remote.stubs(:[]).with(:path).returns "foo" + + @file.expects(:recurse_remote).with { |hash| hash["remote"] = remote } + remote.expects(:[]=).with(:ensure, :absent).never + + @file.recurse + end + end + + describe "and making a new child resource" do + it "should not copy the parent resource's parent" do + Puppet::Type.type(:file).expects(:new).with { |options| ! options.include?(:parent) } + @file.newchild("my/path") + end + + {:recurse => true, :target => "/foo/bar", :ensure => :present, :alias => "yay", :source => "/foo/bar"}.each do |param, value| + it "should not pass on #{param} to the sub resource" do + @file = Puppet::Type::File.new(:name => @path, param => value, :catalog => @catalog) + + @file.class.expects(:new).with { |params| params[param].nil? } + + @file.newchild("sub/file") + end + end + + it "should copy all of the parent resource's 'should' values that were set at initialization" do + file = @file.class.new(:path => "/foo/bar", :owner => "root", :group => "wheel") + @catalog.add_resource(file) + file.class.expects(:new).with { |options| options[:owner] == "root" and options[:group] == "wheel" } + file.newchild("my/path") + end + + it "should not copy default values to the new child" do + @file.class.expects(:new).with { |params| params[:backup].nil? } + @file.newchild("my/path") + end + + it "should not copy values to the child which were set by the source" do + @file[:source] = "/foo/bar" + metadata = stub 'metadata', :owner => "root", :group => "root", :mode => 0755, :ftype => "file", :checksum => "{md5}whatever" + @file.parameter(:source).stubs(:metadata).returns metadata + + @file.parameter(:source).copy_source_values + + @file.class.expects(:new).with { |params| params[:group].nil? } + @file.newchild("my/path") + end + end + end + + describe "when setting the backup" do + it "should default to 'puppet'" do + Puppet::Type::File.new(:name => "/my/file")[:backup].should == "puppet" + end + + it "should allow setting backup to 'false'" do + (!Puppet::Type::File.new(:name => "/my/file", :backup => false)[:backup]).should be_true + end + + it "should set the backup to '.puppet-bak' if it is set to true" do + Puppet::Type::File.new(:name => "/my/file", :backup => true)[:backup].should == ".puppet-bak" + end + + it "should support any other backup extension" do + Puppet::Type::File.new(:name => "/my/file", :backup => ".bak")[:backup].should == ".bak" + end + + it "should set the filebucket when backup is set to a string matching the name of a filebucket in the catalog" do + catalog = Puppet::Resource::Catalog.new + bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" + catalog.add_resource bucket_resource + + file = Puppet::Type::File.new(:name => "/my/file") + catalog.add_resource file + + file[:backup] = "foo" + file.bucket.should == bucket_resource.bucket + end + + it "should find filebuckets added to the catalog after the file resource was created" do + catalog = Puppet::Resource::Catalog.new + + file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") + catalog.add_resource file + + bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" + catalog.add_resource bucket_resource + + file.bucket.should == bucket_resource.bucket + end + + it "should have a nil filebucket if backup is false" do + catalog = Puppet::Resource::Catalog.new + bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" + catalog.add_resource bucket_resource + + file = Puppet::Type::File.new(:name => "/my/file", :backup => false) + catalog.add_resource file + + file.bucket.should be_nil + end + + it "should have a nil filebucket if backup is set to a string starting with '.'" do + catalog = Puppet::Resource::Catalog.new + bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" + catalog.add_resource bucket_resource + + file = Puppet::Type::File.new(:name => "/my/file", :backup => ".foo") + catalog.add_resource file + + file.bucket.should be_nil + end + + it "should fail if there's no catalog and backup is not false" do + file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") + + lambda { file.bucket }.should raise_error(Puppet::Error) + end + + it "should fail if a non-existent catalog is specified" do + file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") + catalog = Puppet::Resource::Catalog.new + catalog.add_resource file + + lambda { file.bucket }.should raise_error(Puppet::Error) + end + + it "should be able to use the default filebucket without a catalog" do + file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") + file.bucket.should be_instance_of(Puppet::FileBucket::Dipper) + end + + it "should look up the filebucket during finish()" do + file = Puppet::Type::File.new(:name => "/my/file", :backup => ".foo") + file.expects(:bucket) + file.finish + end + end + + describe "when retrieving the current file state" do + it "should copy the source values if the 'source' parameter is set" do + file = Puppet::Type::File.new(:name => "/my/file", :source => "/foo/bar") + file.parameter(:source).expects(:copy_source_values) + file.retrieve + end + end +end diff --git a/spec/unit/type/file_spec_spec.rb b/spec/unit/type/file_spec_spec.rb deleted file mode 100755 index 7014854c1..000000000 --- a/spec/unit/type/file_spec_spec.rb +++ /dev/null @@ -1,1053 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Type.type(:file) do - before do - Puppet.settings.stubs(:use) - @real_posix = Puppet.features.posix? - Puppet.features.stubs("posix?").returns(true) - - @path = Tempfile.new("puppetspec") - pathname = @path.path - @path.close!() - @path = pathname - @file = Puppet::Type::File.new(:name => @path) - - @catalog = Puppet::Resource::Catalog.new - @file.catalog = @catalog - end - - describe "when determining if recursion is enabled" do - it "should default to recursion being disabled" do - @file.should_not be_recurse - end - [true, "true", 10, "inf", "remote"].each do |value| - it "should consider #{value} to enable recursion" do - @file[:recurse] = value - @file.must be_recurse - end - end - - [false, "false", 0].each do |value| - it "should consider #{value} to disable recursion" do - @file[:recurse] = value - @file.should_not be_recurse - end - end - end - - describe "#write" do - - it "should propagate failures encountered when renaming the temporary file" do - File.stubs(:open) - - File.expects(:rename).raises ArgumentError - file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") - - file.stubs(:validate_checksum?).returns(false) - - property = stub('content_property', :actual_content => "something", :length => "something".length) - file.stubs(:property).with(:content).returns(property) - - lambda { file.write(:content) }.should raise_error(Puppet::Error) - end - - it "should delegate writing to the content property" do - filehandle = stub_everything 'fh' - File.stubs(:open).yields(filehandle) - File.stubs(:rename) - property = stub('content_property', :actual_content => "something", :length => "something".length) - file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") - file.stubs(:validate_checksum?).returns(false) - file.stubs(:property).with(:content).returns(property) - - property.expects(:write).with(filehandle) - - file.write(:content) - end - - describe "when validating the checksum" do - before { @file.stubs(:validate_checksum?).returns(true) } - - it "should fail if the checksum parameter and content checksums do not match" do - checksum = stub('checksum_parameter', :sum => 'checksum_b') - @file.stubs(:parameter).with(:checksum).returns(checksum) - - property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') - @file.stubs(:property).with(:content).returns(property) - - lambda { @file.write :NOTUSED }.should raise_error(Puppet::Error) - end - end - - describe "when not validating the checksum" do - before { @file.stubs(:validate_checksum?).returns(false) } - - it "should not fail if the checksum property and content checksums do not match" do - checksum = stub('checksum_parameter', :sum => 'checksum_b') - @file.stubs(:parameter).with(:checksum).returns(checksum) - - property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') - @file.stubs(:property).with(:content).returns(property) - - lambda { @file.write :NOTUSED }.should_not raise_error(Puppet::Error) - end - - end - end - - it "should have a method for determining if the file is present" do - @file.must respond_to(:exist?) - end - - it "should be considered existent if it can be stat'ed" do - @file.expects(:stat).returns mock('stat') - @file.must be_exist - end - - it "should be considered nonexistent if it can not be stat'ed" do - @file.expects(:stat).returns nil - @file.must_not be_exist - end - - it "should have a method for determining if the file should be a normal file" do - @file.must respond_to(:should_be_file?) - end - - it "should be a file if :ensure is set to :file" do - @file[:ensure] = :file - @file.must be_should_be_file - end - - it "should be a file if :ensure is set to :present and the file exists as a normal file" do - @file.stubs(:stat).returns(mock('stat', :ftype => "file")) - @file[:ensure] = :present - @file.must be_should_be_file - end - - it "should not be a file if :ensure is set to something other than :file" do - @file[:ensure] = :directory - @file.must_not be_should_be_file - end - - it "should not be a file if :ensure is set to :present and the file exists but is not a normal file" do - @file.stubs(:stat).returns(mock('stat', :ftype => "directory")) - @file[:ensure] = :present - @file.must_not be_should_be_file - end - - it "should be a file if :ensure is not set and :content is" do - @file[:content] = "foo" - @file.must be_should_be_file - end - - it "should be a file if neither :ensure nor :content is set but the file exists as a normal file" do - @file.stubs(:stat).returns(mock("stat", :ftype => "file")) - @file.must be_should_be_file - end - - it "should not be a file if neither :ensure nor :content is set but the file exists but not as a normal file" do - @file.stubs(:stat).returns(mock("stat", :ftype => "directory")) - @file.must_not be_should_be_file - end - - describe "when using POSIX filenames" do - describe "on POSIX systems" do - before do - Puppet.features.stubs(:posix?).returns(true) - Puppet.features.stubs(:win32?).returns(false) - end - - it "should autorequire its parent directory" do - file = Puppet::Type::File.new(:path => "/foo/bar") - dir = Puppet::Type::File.new(:path => "/foo") - @catalog.add_resource file - @catalog.add_resource dir - reqs = file.autorequire - reqs[0].source.must == dir - reqs[0].target.must == file - end - - it "should not autorequire its parent dir if its parent dir is itself" do - file = Puppet::Type::File.new(:path => "/") - @catalog.add_resource file - file.autorequire.should be_empty - end - - it "should remove trailing slashes" do - file = Puppet::Type::File.new(:path => "/foo/bar/baz/") - file[:path].should == "/foo/bar/baz" - end - - it "should remove double slashes" do - file = Puppet::Type::File.new(:path => "/foo/bar//baz") - file[:path].should == "/foo/bar/baz" - end - - it "should remove trailing double slashes" do - file = Puppet::Type::File.new(:path => "/foo/bar/baz//") - file[:path].should == "/foo/bar/baz" - end - - it "should leave a single slash alone" do - file = Puppet::Type::File.new(:path => "/") - file[:path].should == "/" - end - end - - describe "on Win32 systems" do - before do - Puppet.features.stubs(:posix?).returns(false) - Puppet.features.stubs(:win32?).returns(true) - end - - it "should refuse to work" do - lambda { Puppet::Type::File.new(:path => "/foo/bar") }.should raise_error(Puppet::Error) - end - end - end - - describe "when using Win32 filenames" do - confine "Only works on Win32" => Puppet.features.win32? - describe "on Win32 systems" do - before do - Puppet.features.stubs(:posix?).returns(false) - Puppet.features.stubs(:win32?).returns(true) - end - - it "should autorequire its parent directory" do - file = Puppet::Type::File.new(:path => "X:/foo/bar") - dir = Puppet::Type::File.new(:path => "X:/foo") - @catalog.add_resource file - @catalog.add_resource dir - reqs = file.autorequire - reqs[0].source.must == dir - reqs[0].target.must == file - end - - it "should not autorequire its parent dir if its parent dir is itself" do - file = Puppet::Type::File.new(:path => "X:/") - @catalog.add_resource file - file.autorequire.should be_empty - end - - it "should remove trailing slashes" do - file = Puppet::Type::File.new(:path => "X:/foo/bar/baz/") - file[:path].should == "X:/foo/bar/baz" - end - - it "should remove double slashes" do - file = Puppet::Type::File.new(:path => "X:/foo/bar//baz") - file[:path].should == "X:/foo/bar/baz" - end - - it "should remove trailing double slashes" do - file = Puppet::Type::File.new(:path => "X:/foo/bar/baz//") - file[:path].should == "X:/foo/bar/baz" - end - - it "should leave a drive letter with a slash alone" do - file = Puppet::Type::File.new(:path => "X:/") - file[:path].should == "X:/" - end - - it "should add a slash to a drive letter" do - file = Puppet::Type::File.new(:path => "X:") - file[:path].should == "X:/" - end - end - - describe "on POSIX systems" do - before do - Puppet.features.stubs(:posix?).returns(true) - Puppet.features.stubs(:win32?).returns(false) - end - - it "should refuse to work" do - lambda { Puppet::Type::File.new(:path => "X:/foo/bar") }.should raise_error(Puppet::Error) - end - end - end - - describe "when using UNC filenames" do - describe "on Win32 systems" do - confine "Only works on Win32" => Puppet.features.win32? - before do - Puppet.features.stubs(:posix?).returns(false) - Puppet.features.stubs(:win32?).returns(true) - end - - it "should autorequire its parent directory" do - file = Puppet::Type::File.new(:path => "//server/foo/bar") - dir = Puppet::Type::File.new(:path => "//server/foo") - @catalog.add_resource file - @catalog.add_resource dir - reqs = file.autorequire - reqs[0].source.must == dir - reqs[0].target.must == file - end - - it "should not autorequire its parent dir if its parent dir is itself" do - file = Puppet::Type::File.new(:path => "//server/foo") - @catalog.add_resource file - puts file.autorequire - file.autorequire.should be_empty - end - - it "should remove trailing slashes" do - file = Puppet::Type::File.new(:path => "//server/foo/bar/baz/") - file[:path].should == "//server/foo/bar/baz" - end - - it "should remove double slashes" do - file = Puppet::Type::File.new(:path => "//server/foo/bar//baz") - file[:path].should == "//server/foo/bar/baz" - end - - it "should remove trailing double slashes" do - file = Puppet::Type::File.new(:path => "//server/foo/bar/baz//") - file[:path].should == "//server/foo/bar/baz" - end - - it "should remove a trailing slash from a sharename" do - file = Puppet::Type::File.new(:path => "//server/foo/") - file[:path].should == "//server/foo" - end - - it "should not modify a sharename" do - file = Puppet::Type::File.new(:path => "//server/foo") - file[:path].should == "//server/foo" - end - end - - describe "on POSIX systems" do - before do - Puppet.features.stubs(:posix?).returns(true) - Puppet.features.stubs(:win32?).returns(false) - end - - it "should refuse to work" do - lambda { Puppet::Type::File.new(:path => "X:/foo/bar") }.should raise_error(Puppet::Error) - end - end - end - - describe "when initializing" do - it "should set a desired 'ensure' value if none is set and 'content' is set" do - file = Puppet::Type::File.new(:name => "/my/file", :content => "/foo/bar") - file[:ensure].should == :file - end - - it "should set a desired 'ensure' value if none is set and 'target' is set" do - file = Puppet::Type::File.new(:name => "/my/file", :target => "/foo/bar") - file[:ensure].should == :symlink - end - end - - describe "when validating attributes" do - %w{path checksum backup recurse recurselimit source replace force ignore links purge sourceselect}.each do |attr| - it "should have a '#{attr}' parameter" do - Puppet::Type.type(:file).attrtype(attr.intern).should == :param - end - end - - %w{content target ensure owner group mode type}.each do |attr| - it "should have a '#{attr}' property" do - Puppet::Type.type(:file).attrtype(attr.intern).should == :property - end - end - - it "should have its 'path' attribute set as its namevar" do - Puppet::Type.type(:file).key_attributes.should == [:path] - end - end - - describe "when managing links" do - require 'puppettest/support/assertions' - include PuppetTest - require 'tempfile' - - if @real_posix - describe "on POSIX systems" do - before do - @basedir = tempfile - Dir.mkdir(@basedir) - @file = File.join(@basedir, "file") - @link = File.join(@basedir, "link") - - File.open(@file, "w", 0644) { |f| f.puts "yayness"; f.flush } - File.symlink(@file, @link) - - @resource = Puppet::Type.type(:file).new( - :path => @link, - :mode => "755" - ) - @catalog.add_resource @resource - end - - after do - remove_tmp_files - end - - it "should default to managing the link" do - @catalog.apply - # I convert them to strings so they display correctly if there's an error. - ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0644 - end - - it "should be able to follow links" do - @resource[:links] = :follow - @catalog.apply - - ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0755 - end - end - else # @real_posix - # should recode tests using expectations instead of using the filesystem - end - - describe "on Win32 systems" do - before do - Puppet.features.stubs(:posix?).returns(false) - Puppet.features.stubs(:win32?).returns(true) - end - - it "should refuse to work with links" - end - end - - it "should be able to retrieve a stat instance for the file it is managing" do - Puppet::Type.type(:file).new(:path => "/foo/bar", :source => "/bar/foo").should respond_to(:stat) - end - - describe "when stat'ing its file" do - before do - @resource = Puppet::Type.type(:file).new(:path => "/foo/bar") - @resource[:links] = :manage # so we always use :lstat - end - - it "should use :stat if it is following links" do - @resource[:links] = :follow - File.expects(:stat) - - @resource.stat - end - - it "should use :lstat if is it not following links" do - @resource[:links] = :manage - File.expects(:lstat) - - @resource.stat - end - - it "should stat the path of the file" do - File.expects(:lstat).with("/foo/bar") - - @resource.stat - end - - # This only happens in testing. - it "should return nil if the stat does not exist" do - File.expects(:lstat).returns nil - - @resource.stat.should be_nil - end - - it "should return nil if the file does not exist" do - File.expects(:lstat).raises(Errno::ENOENT) - - @resource.stat.should be_nil - end - - it "should return nil if the file cannot be stat'ed" do - File.expects(:lstat).raises(Errno::EACCES) - - @resource.stat.should be_nil - end - - it "should return the stat instance" do - File.expects(:lstat).returns "mystat" - - @resource.stat.should == "mystat" - end - - it "should cache the stat instance if it has a catalog and is applying" do - stat = mock 'stat' - File.expects(:lstat).returns stat - - catalog = Puppet::Resource::Catalog.new - @resource.catalog = catalog - - catalog.stubs(:applying?).returns true - - @resource.stat.should equal(@resource.stat) - end - end - - describe "when flushing" do - it "should flush all properties that respond to :flush" do - @resource = Puppet::Type.type(:file).new(:path => "/foo/bar", :source => "/bar/foo") - @resource.parameter(:source).expects(:flush) - @resource.flush - end - - it "should reset its stat reference" do - @resource = Puppet::Type.type(:file).new(:path => "/foo/bar") - File.expects(:lstat).times(2).returns("stat1").then.returns("stat2") - @resource.stat.should == "stat1" - @resource.flush - @resource.stat.should == "stat2" - end - end - - it "should have a method for performing recursion" do - @file.must respond_to(:perform_recursion) - end - - describe "when executing a recursive search" do - it "should use Metadata to do its recursion" do - Puppet::FileServing::Metadata.expects(:search) - @file.perform_recursion(@file[:path]) - end - - it "should use the provided path as the key to the search" do - Puppet::FileServing::Metadata.expects(:search).with { |key, options| key == "/foo" } - @file.perform_recursion("/foo") - end - - it "should return the results of the metadata search" do - Puppet::FileServing::Metadata.expects(:search).returns "foobar" - @file.perform_recursion(@file[:path]).should == "foobar" - end - - it "should pass its recursion value to the search" do - @file[:recurse] = true - Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:recurse] == true } - @file.perform_recursion(@file[:path]) - end - - it "should pass true if recursion is remote" do - @file[:recurse] = :remote - Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:recurse] == true } - @file.perform_recursion(@file[:path]) - end - - it "should pass its recursion limit value to the search" do - @file[:recurselimit] = 10 - Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:recurselimit] == 10 } - @file.perform_recursion(@file[:path]) - end - - it "should configure the search to ignore or manage links" do - @file[:links] = :manage - Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:links] == :manage } - @file.perform_recursion(@file[:path]) - end - - it "should pass its 'ignore' setting to the search if it has one" do - @file[:ignore] = %w{.svn CVS} - Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:ignore] == %w{.svn CVS} } - @file.perform_recursion(@file[:path]) - end - end - - it "should have a method for performing local recursion" do - @file.must respond_to(:recurse_local) - end - - describe "when doing local recursion" do - before do - @metadata = stub 'metadata', :relative_path => "my/file" - end - - it "should pass its path to the :perform_recursion method" do - @file.expects(:perform_recursion).with(@file[:path]).returns [@metadata] - @file.stubs(:newchild) - @file.recurse_local - end - - it "should return an empty hash if the recursion returns nothing" do - @file.expects(:perform_recursion).returns nil - @file.recurse_local.should == {} - end - - it "should create a new child resource with each generated metadata instance's relative path" do - @file.expects(:perform_recursion).returns [@metadata] - @file.expects(:newchild).with(@metadata.relative_path).returns "fiebar" - @file.recurse_local - end - - it "should not create a new child resource for the '.' directory" do - @metadata.stubs(:relative_path).returns "." - - @file.expects(:perform_recursion).returns [@metadata] - @file.expects(:newchild).never - @file.recurse_local - end - - it "should return a hash of the created resources with the relative paths as the hash keys" do - @file.expects(:perform_recursion).returns [@metadata] - @file.expects(:newchild).with("my/file").returns "fiebar" - @file.recurse_local.should == {"my/file" => "fiebar"} - end - - it "should set checksum_type to none if this file checksum is none" do - @file[:checksum] = :none - Puppet::FileServing::Metadata.expects(:search).with { |path,params| params[:checksum_type] == :none }.returns [@metadata] - @file.expects(:newchild).with("my/file").returns "fiebar" - @file.recurse_local - end - end - - it "should have a method for performing link recursion" do - @file.must respond_to(:recurse_link) - end - - describe "when doing link recursion" do - before do - @first = stub 'first', :relative_path => "first", :full_path => "/my/first", :ftype => "directory" - @second = stub 'second', :relative_path => "second", :full_path => "/my/second", :ftype => "file" - - @resource = stub 'file', :[]= => nil - end - - it "should pass its target to the :perform_recursion method" do - @file[:target] = "mylinks" - @file.expects(:perform_recursion).with("mylinks").returns [@first] - @file.stubs(:newchild).returns @resource - @file.recurse_link({}) - end - - it "should ignore the recursively-found '.' file and configure the top-level file to create a directory" do - @first.stubs(:relative_path).returns "." - @file[:target] = "mylinks" - @file.expects(:perform_recursion).with("mylinks").returns [@first] - @file.stubs(:newchild).never - @file.expects(:[]=).with(:ensure, :directory) - @file.recurse_link({}) - end - - it "should create a new child resource for each generated metadata instance's relative path that doesn't already exist in the children hash" do - @file.expects(:perform_recursion).returns [@first, @second] - @file.expects(:newchild).with(@first.relative_path).returns @resource - @file.recurse_link("second" => @resource) - end - - it "should not create a new child resource for paths that already exist in the children hash" do - @file.expects(:perform_recursion).returns [@first] - @file.expects(:newchild).never - @file.recurse_link("first" => @resource) - end - - it "should set the target to the full path of discovered file and set :ensure to :link if the file is not a directory" do - file = stub 'file' - file.expects(:[]=).with(:target, "/my/second") - file.expects(:[]=).with(:ensure, :link) - - @file.stubs(:perform_recursion).returns [@first, @second] - @file.recurse_link("first" => @resource, "second" => file) - end - - it "should :ensure to :directory if the file is a directory" do - file = stub 'file' - file.expects(:[]=).with(:ensure, :directory) - - @file.stubs(:perform_recursion).returns [@first, @second] - @file.recurse_link("first" => file, "second" => @resource) - end - - it "should return a hash with both created and existing resources with the relative paths as the hash keys" do - file = stub 'file', :[]= => nil - - @file.expects(:perform_recursion).returns [@first, @second] - @file.stubs(:newchild).returns file - @file.recurse_link("second" => @resource).should == {"second" => @resource, "first" => file} - end - end - - it "should have a method for performing remote recursion" do - @file.must respond_to(:recurse_remote) - end - - describe "when doing remote recursion" do - before do - @file[:source] = "puppet://foo/bar" - - @first = Puppet::FileServing::Metadata.new("/my", :relative_path => "first") - @second = Puppet::FileServing::Metadata.new("/my", :relative_path => "second") - @first.stubs(:ftype).returns "directory" - @second.stubs(:ftype).returns "directory" - - @parameter = stub 'property', :metadata= => nil - @resource = stub 'file', :[]= => nil, :parameter => @parameter - end - - it "should pass its source to the :perform_recursion method" do - data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar") - @file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] - @file.stubs(:newchild).returns @resource - @file.recurse_remote({}) - end - - it "should not recurse when the remote file is not a directory" do - data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => ".") - data.stubs(:ftype).returns "file" - @file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] - @file.expects(:newchild).never - @file.recurse_remote({}) - end - - it "should set the source of each returned file to the searched-for URI plus the found relative path" do - @first.expects(:source=).with File.join("puppet://foo/bar", @first.relative_path) - @file.expects(:perform_recursion).returns [@first] - @file.stubs(:newchild).returns @resource - @file.recurse_remote({}) - end - - it "should create a new resource for any relative file paths that do not already have a resource" do - @file.stubs(:perform_recursion).returns [@first] - @file.expects(:newchild).with("first").returns @resource - @file.recurse_remote({}).should == {"first" => @resource} - end - - it "should not create a new resource for any relative file paths that do already have a resource" do - @file.stubs(:perform_recursion).returns [@first] - @file.expects(:newchild).never - @file.recurse_remote("first" => @resource) - end - - it "should set the source of each resource to the source of the metadata" do - @file.stubs(:perform_recursion).returns [@first] - @resource.stubs(:[]=) - @resource.expects(:[]=).with(:source, File.join("puppet://foo/bar", @first.relative_path)) - @file.recurse_remote("first" => @resource) - end - - # LAK:FIXME This is a bug, but I can't think of a fix for it. Fortunately it's already - # filed, and when it's fixed, we'll just fix the whole flow. - it "should set the checksum type to :md5 if the remote file is a file" do - @first.stubs(:ftype).returns "file" - @file.stubs(:perform_recursion).returns [@first] - @resource.stubs(:[]=) - @resource.expects(:[]=).with(:checksum, :md5) - @file.recurse_remote("first" => @resource) - end - - it "should store the metadata in the source property for each resource so the source does not have to requery the metadata" do - @file.stubs(:perform_recursion).returns [@first] - @resource.expects(:parameter).with(:source).returns @parameter - - @parameter.expects(:metadata=).with(@first) - - @file.recurse_remote("first" => @resource) - end - - it "should not create a new resource for the '.' file" do - @first.stubs(:relative_path).returns "." - @file.stubs(:perform_recursion).returns [@first] - - @file.expects(:newchild).never - - @file.recurse_remote({}) - end - - it "should store the metadata in the main file's source property if the relative path is '.'" do - @first.stubs(:relative_path).returns "." - @file.stubs(:perform_recursion).returns [@first] - - @file.parameter(:source).expects(:metadata=).with @first - - @file.recurse_remote("first" => @resource) - end - - describe "and multiple sources are provided" do - describe "and :sourceselect is set to :first" do - it "should create file instances for the results for the first source to return any values" do - data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar") - @file[:source] = %w{/one /two /three /four} - @file.expects(:perform_recursion).with("/one").returns nil - @file.expects(:perform_recursion).with("/two").returns [] - @file.expects(:perform_recursion).with("/three").returns [data] - @file.expects(:perform_recursion).with("/four").never - @file.expects(:newchild).with("foobar").returns @resource - @file.recurse_remote({}) - end - end - - describe "and :sourceselect is set to :all" do - before do - @file[:sourceselect] = :all - end - - it "should return every found file that is not in a previous source" do - klass = Puppet::FileServing::Metadata - @file[:source] = %w{/one /two /three /four} - @file.stubs(:newchild).returns @resource - - one = [klass.new("/one", :relative_path => "a")] - @file.expects(:perform_recursion).with("/one").returns one - @file.expects(:newchild).with("a").returns @resource - - two = [klass.new("/two", :relative_path => "a"), klass.new("/two", :relative_path => "b")] - @file.expects(:perform_recursion).with("/two").returns two - @file.expects(:newchild).with("b").returns @resource - - three = [klass.new("/three", :relative_path => "a"), klass.new("/three", :relative_path => "c")] - @file.expects(:perform_recursion).with("/three").returns three - @file.expects(:newchild).with("c").returns @resource - - @file.expects(:perform_recursion).with("/four").returns [] - - @file.recurse_remote({}) - end - end - end - end - - describe "when returning resources with :eval_generate" do - before do - @graph = stub 'graph', :add_edge => nil - @catalog.stubs(:relationship_graph).returns @graph - - @file.catalog = @catalog - @file[:recurse] = true - end - - it "should recurse if recursion is enabled" do - resource = stub('resource', :[] => "resource") - @file.expects(:recurse?).returns true - @file.expects(:recurse).returns [resource] - @file.eval_generate.should == [resource] - end - - it "should not recurse if recursion is disabled" do - @file.expects(:recurse?).returns false - @file.expects(:recurse).never - @file.eval_generate.should == [] - end - - it "should return each resource found through recursion" do - foo = stub 'foo', :[] => "/foo" - bar = stub 'bar', :[] => "/bar" - bar2 = stub 'bar2', :[] => "/bar" - - @file.expects(:recurse).returns [foo, bar] - - @file.eval_generate.should == [foo, bar] - end - end - - describe "when recursing" do - before do - @file[:recurse] = true - @metadata = Puppet::FileServing::Metadata - end - - describe "and a source is set" do - before { @file[:source] = "/my/source" } - - it "should pass the already-discovered resources to recurse_remote" do - @file.stubs(:recurse_local).returns(:foo => "bar") - @file.expects(:recurse_remote).with(:foo => "bar").returns [] - @file.recurse - end - end - - describe "and a target is set" do - before { @file[:target] = "/link/target" } - - it "should use recurse_link" do - @file.stubs(:recurse_local).returns(:foo => "bar") - @file.expects(:recurse_link).with(:foo => "bar").returns [] - @file.recurse - end - end - - it "should use recurse_local if recurse is not remote" do - @file.expects(:recurse_local).returns({}) - @file.recurse - end - - it "should not use recurse_local if recurse remote" do - @file[:recurse] = :remote - @file.expects(:recurse_local).never - @file.recurse - end - - it "should return the generated resources as an array sorted by file path" do - one = stub 'one', :[] => "/one" - two = stub 'two', :[] => "/one/two" - three = stub 'three', :[] => "/three" - @file.expects(:recurse_local).returns(:one => one, :two => two, :three => three) - @file.recurse.should == [one, two, three] - end - - describe "and purging is enabled" do - before do - @file[:purge] = true - end - - it "should configure each file to be removed" do - local = stub 'local' - local.stubs(:[]).with(:source).returns nil # Thus, a local file - local.stubs(:[]).with(:path).returns "foo" - @file.expects(:recurse_local).returns("local" => local) - local.expects(:[]=).with(:ensure, :absent) - - @file.recurse - end - - it "should not remove files that exist in the remote repository" do - @file["source"] = "/my/file" - @file.expects(:recurse_local).returns({}) - - remote = stub 'remote' - remote.stubs(:[]).with(:source).returns "/whatever" # Thus, a remote file - remote.stubs(:[]).with(:path).returns "foo" - - @file.expects(:recurse_remote).with { |hash| hash["remote"] = remote } - remote.expects(:[]=).with(:ensure, :absent).never - - @file.recurse - end - end - - describe "and making a new child resource" do - it "should not copy the parent resource's parent" do - Puppet::Type.type(:file).expects(:new).with { |options| ! options.include?(:parent) } - @file.newchild("my/path") - end - - {:recurse => true, :target => "/foo/bar", :ensure => :present, :alias => "yay", :source => "/foo/bar"}.each do |param, value| - it "should not pass on #{param} to the sub resource" do - @file = Puppet::Type::File.new(:name => @path, param => value, :catalog => @catalog) - - @file.class.expects(:new).with { |params| params[param].nil? } - - @file.newchild("sub/file") - end - end - - it "should copy all of the parent resource's 'should' values that were set at initialization" do - file = @file.class.new(:path => "/foo/bar", :owner => "root", :group => "wheel") - @catalog.add_resource(file) - file.class.expects(:new).with { |options| options[:owner] == "root" and options[:group] == "wheel" } - file.newchild("my/path") - end - - it "should not copy default values to the new child" do - @file.class.expects(:new).with { |params| params[:backup].nil? } - @file.newchild("my/path") - end - - it "should not copy values to the child which were set by the source" do - @file[:source] = "/foo/bar" - metadata = stub 'metadata', :owner => "root", :group => "root", :mode => 0755, :ftype => "file", :checksum => "{md5}whatever" - @file.parameter(:source).stubs(:metadata).returns metadata - - @file.parameter(:source).copy_source_values - - @file.class.expects(:new).with { |params| params[:group].nil? } - @file.newchild("my/path") - end - end - end - - describe "when setting the backup" do - it "should default to 'puppet'" do - Puppet::Type::File.new(:name => "/my/file")[:backup].should == "puppet" - end - - it "should allow setting backup to 'false'" do - (!Puppet::Type::File.new(:name => "/my/file", :backup => false)[:backup]).should be_true - end - - it "should set the backup to '.puppet-bak' if it is set to true" do - Puppet::Type::File.new(:name => "/my/file", :backup => true)[:backup].should == ".puppet-bak" - end - - it "should support any other backup extension" do - Puppet::Type::File.new(:name => "/my/file", :backup => ".bak")[:backup].should == ".bak" - end - - it "should set the filebucket when backup is set to a string matching the name of a filebucket in the catalog" do - catalog = Puppet::Resource::Catalog.new - bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" - catalog.add_resource bucket_resource - - file = Puppet::Type::File.new(:name => "/my/file") - catalog.add_resource file - - file[:backup] = "foo" - file.bucket.should == bucket_resource.bucket - end - - it "should find filebuckets added to the catalog after the file resource was created" do - catalog = Puppet::Resource::Catalog.new - - file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") - catalog.add_resource file - - bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" - catalog.add_resource bucket_resource - - file.bucket.should == bucket_resource.bucket - end - - it "should have a nil filebucket if backup is false" do - catalog = Puppet::Resource::Catalog.new - bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" - catalog.add_resource bucket_resource - - file = Puppet::Type::File.new(:name => "/my/file", :backup => false) - catalog.add_resource file - - file.bucket.should be_nil - end - - it "should have a nil filebucket if backup is set to a string starting with '.'" do - catalog = Puppet::Resource::Catalog.new - bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" - catalog.add_resource bucket_resource - - file = Puppet::Type::File.new(:name => "/my/file", :backup => ".foo") - catalog.add_resource file - - file.bucket.should be_nil - end - - it "should fail if there's no catalog and backup is not false" do - file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") - - lambda { file.bucket }.should raise_error(Puppet::Error) - end - - it "should fail if a non-existent catalog is specified" do - file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") - catalog = Puppet::Resource::Catalog.new - catalog.add_resource file - - lambda { file.bucket }.should raise_error(Puppet::Error) - end - - it "should be able to use the default filebucket without a catalog" do - file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") - file.bucket.should be_instance_of(Puppet::FileBucket::Dipper) - end - - it "should look up the filebucket during finish()" do - file = Puppet::Type::File.new(:name => "/my/file", :backup => ".foo") - file.expects(:bucket) - file.finish - end - end - - describe "when retrieving the current file state" do - it "should copy the source values if the 'source' parameter is set" do - file = Puppet::Type::File.new(:name => "/my/file", :source => "/foo/bar") - file.parameter(:source).expects(:copy_source_values) - file.retrieve - end - end -end diff --git a/spec/unit/type/filebucket_spec.rb b/spec/unit/type/filebucket_spec.rb new file mode 100644 index 000000000..f3efc98b0 --- /dev/null +++ b/spec/unit/type/filebucket_spec.rb @@ -0,0 +1,74 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type.type(:filebucket) do + describe "when validating attributes" do + %w{name server port path}.each do |attr| + it "should have a '#{attr}' parameter" do + Puppet::Type.type(:filebucket).attrtype(attr.intern).should == :param + end + end + + it "should have its 'name' attribute set as its namevar" do + Puppet::Type.type(:filebucket).key_attributes.should == [:name] + end + end + + it "should use the clientbucketdir as the path by default path" do + Puppet.settings[:clientbucketdir] = "/my/bucket" + Puppet::Type.type(:filebucket).new(:name => "main")[:path].should == Puppet[:clientbucketdir] + end + + it "should use the masterport as the path by default port" do + Puppet.settings[:masterport] = 50 + Puppet::Type.type(:filebucket).new(:name => "main")[:port].should == Puppet[:masterport] + end + + it "should use the server as the path by default server" do + Puppet.settings[:server] = "myserver" + Puppet::Type.type(:filebucket).new(:name => "main")[:server].should == Puppet[:server] + end + + it "be local by default" do + bucket = Puppet::Type.type(:filebucket).new :name => "main" + + bucket.bucket.should be_local + end + + it "not be local if path is false" do + bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => false + + bucket.bucket.should_not be_local + end + + it "be local if both a path and a server are specified" do + bucket = Puppet::Type.type(:filebucket).new :name => "main", :server => "puppet", :path => "/my/path" + + bucket.bucket.should be_local + end + + describe "when creating the filebucket" do + before do + @bucket = stub 'bucket', :name= => nil + end + + it "should use any provided path" do + bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => "/foo/bar" + Puppet::FileBucket::Dipper.expects(:new).with(:Path => "/foo/bar").returns @bucket + bucket.bucket + end + it "should use any provided server and port" do + bucket = Puppet::Type.type(:filebucket).new :name => "main", :server => "myserv", :port => "myport", :path => false + Puppet::FileBucket::Dipper.expects(:new).with(:Server => "myserv", :Port => "myport").returns @bucket + bucket.bucket + end + + it "should use the default server if the path is unset and no server is provided" do + Puppet.settings[:server] = "myserv" + bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => false + Puppet::FileBucket::Dipper.expects(:new).with { |args| args[:Server] == "myserv" }.returns @bucket + bucket.bucket + end + end +end diff --git a/spec/unit/type/filebucket_spec_spec.rb b/spec/unit/type/filebucket_spec_spec.rb deleted file mode 100644 index f3efc98b0..000000000 --- a/spec/unit/type/filebucket_spec_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Type.type(:filebucket) do - describe "when validating attributes" do - %w{name server port path}.each do |attr| - it "should have a '#{attr}' parameter" do - Puppet::Type.type(:filebucket).attrtype(attr.intern).should == :param - end - end - - it "should have its 'name' attribute set as its namevar" do - Puppet::Type.type(:filebucket).key_attributes.should == [:name] - end - end - - it "should use the clientbucketdir as the path by default path" do - Puppet.settings[:clientbucketdir] = "/my/bucket" - Puppet::Type.type(:filebucket).new(:name => "main")[:path].should == Puppet[:clientbucketdir] - end - - it "should use the masterport as the path by default port" do - Puppet.settings[:masterport] = 50 - Puppet::Type.type(:filebucket).new(:name => "main")[:port].should == Puppet[:masterport] - end - - it "should use the server as the path by default server" do - Puppet.settings[:server] = "myserver" - Puppet::Type.type(:filebucket).new(:name => "main")[:server].should == Puppet[:server] - end - - it "be local by default" do - bucket = Puppet::Type.type(:filebucket).new :name => "main" - - bucket.bucket.should be_local - end - - it "not be local if path is false" do - bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => false - - bucket.bucket.should_not be_local - end - - it "be local if both a path and a server are specified" do - bucket = Puppet::Type.type(:filebucket).new :name => "main", :server => "puppet", :path => "/my/path" - - bucket.bucket.should be_local - end - - describe "when creating the filebucket" do - before do - @bucket = stub 'bucket', :name= => nil - end - - it "should use any provided path" do - bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => "/foo/bar" - Puppet::FileBucket::Dipper.expects(:new).with(:Path => "/foo/bar").returns @bucket - bucket.bucket - end - it "should use any provided server and port" do - bucket = Puppet::Type.type(:filebucket).new :name => "main", :server => "myserv", :port => "myport", :path => false - Puppet::FileBucket::Dipper.expects(:new).with(:Server => "myserv", :Port => "myport").returns @bucket - bucket.bucket - end - - it "should use the default server if the path is unset and no server is provided" do - Puppet.settings[:server] = "myserv" - bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => false - Puppet::FileBucket::Dipper.expects(:new).with { |args| args[:Server] == "myserv" }.returns @bucket - bucket.bucket - end - end -end diff --git a/spec/unit/type/group_spec.rb b/spec/unit/type/group_spec.rb new file mode 100755 index 000000000..0bd7c513d --- /dev/null +++ b/spec/unit/type/group_spec.rb @@ -0,0 +1,59 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type.type(:group) do + before do + unless ENV["PATH"].split(File::PATH_SEPARATOR).include?("/usr/sbin") + ENV["PATH"] += File::PATH_SEPARATOR + "/usr/sbin" + end + @class = Puppet::Type.type(:group) + end + + it "should have a default provider" do + @class.defaultprovider.should_not be_nil + end + + it "should have a default provider inheriting from Puppet::Provider" do + @class.defaultprovider.ancestors.should be_include(Puppet::Provider) + end + + describe "when validating attributes" do + [:name, :allowdupe].each do |param| + it "should have a #{param} parameter" do + @class.attrtype(param).should == :param + end + end + + [:ensure, :gid].each do |param| + it "should have a #{param} property" do + @class.attrtype(param).should == :property + end + end + + it "should convert gids provided as strings into integers" do + @class.new(:name => "foo", :gid => "15")[:gid].should == 15 + end + + it "should accepts gids provided as integers" do + @class.new(:name => "foo", :gid => 15)[:gid].should == 15 + end + end + + # #1407 - we need to declare the allowdupe param as boolean. + it "should have a boolean method for determining if duplicates are allowed" do + @class.new(:name => "foo").methods.should be_include("allowdupe?") + end + + it "should call 'create' to create the group" do + group = @class.new(:name => "foo", :ensure => :present) + group.provider.expects(:create) + group.parameter(:ensure).sync + end + + it "should call 'delete' to remove the group" do + group = @class.new(:name => "foo", :ensure => :absent) + group.provider.expects(:delete) + group.parameter(:ensure).sync + end +end diff --git a/spec/unit/type/group_spec_spec.rb b/spec/unit/type/group_spec_spec.rb deleted file mode 100755 index 0bd7c513d..000000000 --- a/spec/unit/type/group_spec_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Type.type(:group) do - before do - unless ENV["PATH"].split(File::PATH_SEPARATOR).include?("/usr/sbin") - ENV["PATH"] += File::PATH_SEPARATOR + "/usr/sbin" - end - @class = Puppet::Type.type(:group) - end - - it "should have a default provider" do - @class.defaultprovider.should_not be_nil - end - - it "should have a default provider inheriting from Puppet::Provider" do - @class.defaultprovider.ancestors.should be_include(Puppet::Provider) - end - - describe "when validating attributes" do - [:name, :allowdupe].each do |param| - it "should have a #{param} parameter" do - @class.attrtype(param).should == :param - end - end - - [:ensure, :gid].each do |param| - it "should have a #{param} property" do - @class.attrtype(param).should == :property - end - end - - it "should convert gids provided as strings into integers" do - @class.new(:name => "foo", :gid => "15")[:gid].should == 15 - end - - it "should accepts gids provided as integers" do - @class.new(:name => "foo", :gid => 15)[:gid].should == 15 - end - end - - # #1407 - we need to declare the allowdupe param as boolean. - it "should have a boolean method for determining if duplicates are allowed" do - @class.new(:name => "foo").methods.should be_include("allowdupe?") - end - - it "should call 'create' to create the group" do - group = @class.new(:name => "foo", :ensure => :present) - group.provider.expects(:create) - group.parameter(:ensure).sync - end - - it "should call 'delete' to remove the group" do - group = @class.new(:name => "foo", :ensure => :absent) - group.provider.expects(:delete) - group.parameter(:ensure).sync - end -end diff --git a/spec/unit/type/macauthorization_spec.rb b/spec/unit/type/macauthorization_spec.rb new file mode 100755 index 000000000..8785984fe --- /dev/null +++ b/spec/unit/type/macauthorization_spec.rb @@ -0,0 +1,111 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +macauth_type = Puppet::Type.type(:macauthorization) + +describe Puppet::Type.type(:macauthorization), "when checking macauthorization objects" do + + before do + authplist = {} + authplist["rules"] = { "foorule" => "foo" } + authplist["rights"] = { "fooright" => "foo" } + provider_class = macauth_type.provider(macauth_type.providers[0]) + Plist.stubs(:parse_xml).with("/etc/authorization").returns(authplist) + macauth_type.stubs(:defaultprovider).returns provider_class + @resource = macauth_type.new(:name => 'foo') + end + + describe "when validating attributes" do + + parameters = [:name,] + properties = [:auth_type, :allow_root, :authenticate_user, :auth_class, + :comment, :group, :k_of_n, :mechanisms, :rule, + :session_owner, :shared, :timeout, :tries] + + parameters.each do |parameter| + it "should have a %s parameter" % parameter do + macauth_type.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) + end + + it "should have documentation for its %s parameter" % parameter do + macauth_type.attrclass(parameter).doc.should be_instance_of(String) + end + end + + properties.each do |property| + it "should have a %s property" % property do + macauth_type.attrclass(property).ancestors.should be_include(Puppet::Property) + end + + it "should have documentation for its %s property" % property do + macauth_type.attrclass(property).doc.should be_instance_of(String) + end + end + + end + + describe "when validating properties" do + + it "should have a default provider inheriting from Puppet::Provider" do + macauth_type.defaultprovider.ancestors.should be_include(Puppet::Provider) + end + + it "should be able to create an instance" do + lambda { + macauth_type.new(:name => 'foo') + }.should_not raise_error + end + + it "should support :present as a value to :ensure" do + lambda { + macauth_type.new(:name => "foo", :ensure => :present) + }.should_not raise_error + end + + it "should support :absent as a value to :ensure" do + lambda { + macauth_type.new(:name => "foo", :ensure => :absent) + }.should_not raise_error + end + + end + + [:k_of_n, :timeout, :tries].each do |property| + describe "when managing the #{property} property" do + it "should convert number-looking strings into actual numbers" do + prop = macauth_type.attrclass(property).new(:resource => @resource) + prop.should = "300" + prop.should.must == 300 + end + it "should support integers as a value" do + prop = macauth_type.attrclass(property).new(:resource => @resource) + prop.should = 300 + prop.should.must == 300 + end + it "should raise an error for non-integer values" do + prop = macauth_type.attrclass(property).new(:resource => @resource) + lambda { prop.should = "foo" }.should raise_error(Puppet::Error) + end + end + end + + [:allow_root, :authenticate_user, :session_owner, :shared].each do |property| + describe "when managing the #{property} property" do + it "should convert boolean-looking false strings into actual booleans" do + prop = macauth_type.attrclass(property).new(:resource => @resource) + prop.should = "false" + prop.should.must == :false + end + it "should convert boolean-looking true strings into actual booleans" do + prop = macauth_type.attrclass(property).new(:resource => @resource) + prop.should = "true" + prop.should.must == :true + end + it "should raise an error for non-boolean values" do + prop = macauth_type.attrclass(property).new(:resource => @resource) + lambda { prop.should = "foo" }.should raise_error(Puppet::Error) + end + end + end +end diff --git a/spec/unit/type/macauthorization_spec_spec.rb b/spec/unit/type/macauthorization_spec_spec.rb deleted file mode 100755 index 8785984fe..000000000 --- a/spec/unit/type/macauthorization_spec_spec.rb +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -macauth_type = Puppet::Type.type(:macauthorization) - -describe Puppet::Type.type(:macauthorization), "when checking macauthorization objects" do - - before do - authplist = {} - authplist["rules"] = { "foorule" => "foo" } - authplist["rights"] = { "fooright" => "foo" } - provider_class = macauth_type.provider(macauth_type.providers[0]) - Plist.stubs(:parse_xml).with("/etc/authorization").returns(authplist) - macauth_type.stubs(:defaultprovider).returns provider_class - @resource = macauth_type.new(:name => 'foo') - end - - describe "when validating attributes" do - - parameters = [:name,] - properties = [:auth_type, :allow_root, :authenticate_user, :auth_class, - :comment, :group, :k_of_n, :mechanisms, :rule, - :session_owner, :shared, :timeout, :tries] - - parameters.each do |parameter| - it "should have a %s parameter" % parameter do - macauth_type.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) - end - - it "should have documentation for its %s parameter" % parameter do - macauth_type.attrclass(parameter).doc.should be_instance_of(String) - end - end - - properties.each do |property| - it "should have a %s property" % property do - macauth_type.attrclass(property).ancestors.should be_include(Puppet::Property) - end - - it "should have documentation for its %s property" % property do - macauth_type.attrclass(property).doc.should be_instance_of(String) - end - end - - end - - describe "when validating properties" do - - it "should have a default provider inheriting from Puppet::Provider" do - macauth_type.defaultprovider.ancestors.should be_include(Puppet::Provider) - end - - it "should be able to create an instance" do - lambda { - macauth_type.new(:name => 'foo') - }.should_not raise_error - end - - it "should support :present as a value to :ensure" do - lambda { - macauth_type.new(:name => "foo", :ensure => :present) - }.should_not raise_error - end - - it "should support :absent as a value to :ensure" do - lambda { - macauth_type.new(:name => "foo", :ensure => :absent) - }.should_not raise_error - end - - end - - [:k_of_n, :timeout, :tries].each do |property| - describe "when managing the #{property} property" do - it "should convert number-looking strings into actual numbers" do - prop = macauth_type.attrclass(property).new(:resource => @resource) - prop.should = "300" - prop.should.must == 300 - end - it "should support integers as a value" do - prop = macauth_type.attrclass(property).new(:resource => @resource) - prop.should = 300 - prop.should.must == 300 - end - it "should raise an error for non-integer values" do - prop = macauth_type.attrclass(property).new(:resource => @resource) - lambda { prop.should = "foo" }.should raise_error(Puppet::Error) - end - end - end - - [:allow_root, :authenticate_user, :session_owner, :shared].each do |property| - describe "when managing the #{property} property" do - it "should convert boolean-looking false strings into actual booleans" do - prop = macauth_type.attrclass(property).new(:resource => @resource) - prop.should = "false" - prop.should.must == :false - end - it "should convert boolean-looking true strings into actual booleans" do - prop = macauth_type.attrclass(property).new(:resource => @resource) - prop.should = "true" - prop.should.must == :true - end - it "should raise an error for non-boolean values" do - prop = macauth_type.attrclass(property).new(:resource => @resource) - lambda { prop.should = "foo" }.should raise_error(Puppet::Error) - end - end - end -end diff --git a/spec/unit/type/maillist_spec.rb b/spec/unit/type/maillist_spec.rb new file mode 100755 index 000000000..585336665 --- /dev/null +++ b/spec/unit/type/maillist_spec.rb @@ -0,0 +1,42 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +maillist = Puppet::Type.type(:maillist) + +describe maillist do + before do + @provider_class = Puppet::Type.type(:maillist).provider(:mailman) + + @provider = stub 'provider', :class => @provider_class, :clear => nil + @provider.stubs(:respond_to).with(:aliases).returns(true) + + @provider_class.stubs(:new).returns(@provider) + + Puppet::Type.type(:maillist).stubs(:defaultprovider).returns(@provider_class) + + @maillist = Puppet::Type.type(:maillist).new( :name => 'test' ) + + @catalog = Puppet::Resource::Catalog.new + @maillist.catalog = @catalog + end + + it "should generate aliases unless they already exist" do + # Mail List aliases are careful not to stomp on managed Mail Alias aliases + + # test1 is an unmanaged alias from /etc/aliases + Puppet::Type.type(:mailalias).provider(:aliases).stubs(:target_object).returns( StringIO.new("test1: root\n") ) + + # test2 is a managed alias from the manifest + dupe = Puppet::Type.type(:mailalias).new( :name => 'test2' ) + @catalog.add_resource dupe + + @provider.stubs(:aliases).returns({"test1" => 'this will get included', "test2" => 'this will dropped', "test3" => 'this will get included'}) + + generated = @maillist.generate + generated.map{ |x| x.name }.sort.should == ['test1', 'test3'] + generated.map{ |x| x.class }.should == [Puppet::Type::Mailalias, Puppet::Type::Mailalias] + + end + +end diff --git a/spec/unit/type/maillist_spec_spec.rb b/spec/unit/type/maillist_spec_spec.rb deleted file mode 100755 index 585336665..000000000 --- a/spec/unit/type/maillist_spec_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -maillist = Puppet::Type.type(:maillist) - -describe maillist do - before do - @provider_class = Puppet::Type.type(:maillist).provider(:mailman) - - @provider = stub 'provider', :class => @provider_class, :clear => nil - @provider.stubs(:respond_to).with(:aliases).returns(true) - - @provider_class.stubs(:new).returns(@provider) - - Puppet::Type.type(:maillist).stubs(:defaultprovider).returns(@provider_class) - - @maillist = Puppet::Type.type(:maillist).new( :name => 'test' ) - - @catalog = Puppet::Resource::Catalog.new - @maillist.catalog = @catalog - end - - it "should generate aliases unless they already exist" do - # Mail List aliases are careful not to stomp on managed Mail Alias aliases - - # test1 is an unmanaged alias from /etc/aliases - Puppet::Type.type(:mailalias).provider(:aliases).stubs(:target_object).returns( StringIO.new("test1: root\n") ) - - # test2 is a managed alias from the manifest - dupe = Puppet::Type.type(:mailalias).new( :name => 'test2' ) - @catalog.add_resource dupe - - @provider.stubs(:aliases).returns({"test1" => 'this will get included', "test2" => 'this will dropped', "test3" => 'this will get included'}) - - generated = @maillist.generate - generated.map{ |x| x.name }.sort.should == ['test1', 'test3'] - generated.map{ |x| x.class }.should == [Puppet::Type::Mailalias, Puppet::Type::Mailalias] - - end - -end diff --git a/spec/unit/type/mcx_spec.rb b/spec/unit/type/mcx_spec.rb new file mode 100755 index 000000000..8b5dfb073 --- /dev/null +++ b/spec/unit/type/mcx_spec.rb @@ -0,0 +1,100 @@ +#!/usr/bin/env ruby +#-- +# Copyright (C) 2008 Jeffrey J McCune. + +# This program and entire repository is free software; you can +# redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software +# Foundation; either version 2 of the License, or any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Author: Jeff McCune + +# Most of this code copied from /spec/type/service.rb + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/type/mcx' + +mcx_type = Puppet::Type.type(:mcx) + +describe mcx_type, "when validating attributes" do + + properties = [:ensure, :content] + parameters = [:name, :ds_type, :ds_name] + + parameters.each do |p| + it "should have a #{p} parameter" do + mcx_type.attrclass(p).ancestors.should be_include(Puppet::Parameter) + end + it "should have documentation for its #{p} parameter" do + mcx_type.attrclass(p).doc.should be_instance_of(String) + end + end + + properties.each do |p| + it "should have a #{p} property" do + mcx_type.attrclass(p).ancestors.should be_include(Puppet::Property) + end + it "should have documentation for its #{p} property" do + mcx_type.attrclass(p).doc.should be_instance_of(String) + end + end + +end + +describe mcx_type, "default values" do + + before :each do + provider_class = mcx_type.provider(mcx_type.providers[0]) + mcx_type.stubs(:defaultprovider).returns provider_class + end + + it "should be nil for :ds_type" do + mcx_type.new(:name => '/Foo/bar')[:ds_type].should be_nil + end + + it "should be nil for :ds_name" do + mcx_type.new(:name => '/Foo/bar')[:ds_name].should be_nil + end + + it "should be nil for :content" do + mcx_type.new(:name => '/Foo/bar')[:content].should be_nil + end + +end + +describe mcx_type, "when validating properties" do + + before :each do + provider_class = mcx_type.provider(mcx_type.providers[0]) + mcx_type.stubs(:defaultprovider).returns provider_class + end + + it "should be able to create an instance" do + lambda { + mcx_type.new(:name => '/Foo/bar') + }.should_not raise_error + end + + it "should support :present as a value to :ensure" do + lambda { + mcx_type.new(:name => "/Foo/bar", :ensure => :present) + }.should_not raise_error + end + + it "should support :absent as a value to :ensure" do + lambda { + mcx_type.new(:name => "/Foo/bar", :ensure => :absent) + }.should_not raise_error + end + +end diff --git a/spec/unit/type/mcx_spec_spec.rb b/spec/unit/type/mcx_spec_spec.rb deleted file mode 100755 index 8b5dfb073..000000000 --- a/spec/unit/type/mcx_spec_spec.rb +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env ruby -#-- -# Copyright (C) 2008 Jeffrey J McCune. - -# This program and entire repository is free software; you can -# redistribute it and/or modify it under the terms of the GNU -# General Public License as published by the Free Software -# Foundation; either version 2 of the License, or any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -# Author: Jeff McCune - -# Most of this code copied from /spec/type/service.rb - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/type/mcx' - -mcx_type = Puppet::Type.type(:mcx) - -describe mcx_type, "when validating attributes" do - - properties = [:ensure, :content] - parameters = [:name, :ds_type, :ds_name] - - parameters.each do |p| - it "should have a #{p} parameter" do - mcx_type.attrclass(p).ancestors.should be_include(Puppet::Parameter) - end - it "should have documentation for its #{p} parameter" do - mcx_type.attrclass(p).doc.should be_instance_of(String) - end - end - - properties.each do |p| - it "should have a #{p} property" do - mcx_type.attrclass(p).ancestors.should be_include(Puppet::Property) - end - it "should have documentation for its #{p} property" do - mcx_type.attrclass(p).doc.should be_instance_of(String) - end - end - -end - -describe mcx_type, "default values" do - - before :each do - provider_class = mcx_type.provider(mcx_type.providers[0]) - mcx_type.stubs(:defaultprovider).returns provider_class - end - - it "should be nil for :ds_type" do - mcx_type.new(:name => '/Foo/bar')[:ds_type].should be_nil - end - - it "should be nil for :ds_name" do - mcx_type.new(:name => '/Foo/bar')[:ds_name].should be_nil - end - - it "should be nil for :content" do - mcx_type.new(:name => '/Foo/bar')[:content].should be_nil - end - -end - -describe mcx_type, "when validating properties" do - - before :each do - provider_class = mcx_type.provider(mcx_type.providers[0]) - mcx_type.stubs(:defaultprovider).returns provider_class - end - - it "should be able to create an instance" do - lambda { - mcx_type.new(:name => '/Foo/bar') - }.should_not raise_error - end - - it "should support :present as a value to :ensure" do - lambda { - mcx_type.new(:name => "/Foo/bar", :ensure => :present) - }.should_not raise_error - end - - it "should support :absent as a value to :ensure" do - lambda { - mcx_type.new(:name => "/Foo/bar", :ensure => :absent) - }.should_not raise_error - end - -end diff --git a/spec/unit/type/mount_spec.rb b/spec/unit/type/mount_spec.rb new file mode 100755 index 000000000..fd9c6cb52 --- /dev/null +++ b/spec/unit/type/mount_spec.rb @@ -0,0 +1,211 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type.type(:mount) do + it "should have a :refreshable feature that requires the :remount method" do + Puppet::Type.type(:mount).provider_feature(:refreshable).methods.should == [:remount] + end + + it "should have no default value for :ensure" do + mount = Puppet::Type.type(:mount).new(:name => "yay") + mount.should(:ensure).should be_nil + end +end + +describe Puppet::Type.type(:mount), "when validating attributes" do + [:name, :remounts].each do |param| + it "should have a #{param} parameter" do + Puppet::Type.type(:mount).attrtype(param).should == :param + end + end + + [:ensure, :device, :blockdevice, :fstype, :options, :pass, :dump, :atboot, :target].each do |param| + it "should have a #{param} property" do + Puppet::Type.type(:mount).attrtype(param).should == :property + end + end +end + +describe Puppet::Type.type(:mount)::Ensure, "when validating values" do + before do + @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil + Puppet::Type.type(:mount).defaultprovider.expects(:new).returns(@provider) + end + + it "should support :present as a value to :ensure" do + Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) + end + + it "should alias :unmounted to :present as a value to :ensure" do + mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :unmounted) + mount.should(:ensure).should == :present + end + + it "should support :absent as a value to :ensure" do + Puppet::Type.type(:mount).new(:name => "yay", :ensure => :absent) + end + + it "should support :mounted as a value to :ensure" do + Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) + end +end + +describe Puppet::Type.type(:mount)::Ensure do + before :each do + @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock + Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) + @mount = Puppet::Type.type(:mount).new(:name => "yay", :check => :ensure) + + @ensure = @mount.property(:ensure) + end + + def mount_stub(params) + Puppet::Type.type(:mount).validproperties.each do |prop| + unless params[prop] + params[prop] = :absent + @mount[prop] = :absent + end + end + + params.each do |param, value| + @provider.stubs(param).returns(value) + end + end + + describe Puppet::Type.type(:mount)::Ensure, "when retrieving its current state" do + + it "should return the provider's value if it is :absent" do + @provider.expects(:ensure).returns(:absent) + @ensure.retrieve.should == :absent + end + + it "should return :mounted if the provider indicates it is mounted and the value is not :absent" do + @provider.expects(:ensure).returns(:present) + @provider.expects(:mounted?).returns(true) + @ensure.retrieve.should == :mounted + end + + it "should return :present if the provider indicates it is not mounted and the value is not :absent" do + @provider.expects(:ensure).returns(:present) + @provider.expects(:mounted?).returns(false) + @ensure.retrieve.should == :present + end + end + + describe Puppet::Type.type(:mount)::Ensure, "when changing the host" do + + it "should destroy itself if it should be absent" do + @provider.stubs(:mounted?).returns(false) + @provider.expects(:destroy) + @ensure.should = :absent + @ensure.sync + end + + it "should unmount itself before destroying if it is mounted and should be absent" do + @provider.expects(:mounted?).returns(true) + @provider.expects(:unmount) + @provider.expects(:destroy) + @ensure.should = :absent + @ensure.sync + end + + it "should create itself if it is absent and should be present" do + @provider.stubs(:mounted?).returns(false) + @provider.expects(:create) + @ensure.should = :present + @ensure.sync + end + + it "should unmount itself if it is mounted and should be present" do + @provider.stubs(:mounted?).returns(true) + + # The interface here is just too much work to test right now. + @ensure.stubs(:syncothers) + @provider.expects(:unmount) + @ensure.should = :present + @ensure.sync + end + + it "should create and mount itself if it does not exist and should be mounted" do + @provider.stubs(:ensure).returns(:absent) + @provider.stubs(:mounted?).returns(false) + @provider.expects(:create) + @ensure.stubs(:syncothers) + @provider.expects(:mount) + @ensure.should = :mounted + @ensure.sync + end + + it "should mount itself if it is present and should be mounted" do + @provider.stubs(:ensure).returns(:present) + @provider.stubs(:mounted?).returns(false) + @ensure.stubs(:syncothers) + @provider.expects(:mount) + @ensure.should = :mounted + @ensure.sync + end + + it "should create but not mount itself if it is absent and mounted and should be mounted" do + @provider.stubs(:ensure).returns(:absent) + @provider.stubs(:mounted?).returns(true) + @ensure.stubs(:syncothers) + @provider.expects(:create) + @ensure.should = :mounted + @ensure.sync + end + end + + describe Puppet::Type.type(:mount), "when responding to events" do + + it "should remount if it is currently mounted" do + @provider.expects(:mounted?).returns(true) + @provider.expects(:remount) + + @mount.refresh + end + + it "should not remount if it is not currently mounted" do + @provider.expects(:mounted?).returns(false) + @provider.expects(:remount).never + + @mount.refresh + end + + it "should not remount swap filesystems" do + @mount[:fstype] = "swap" + @provider.expects(:remount).never + + @mount.refresh + end + end +end + +describe Puppet::Type.type(:mount), "when modifying an existing mount entry" do + before do + @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :remount => nil + Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) + @mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) + + {:device => "/foo/bar", :blockdevice => "/other/bar", :target => "/what/ever", :fstype => 'eh', :options => "", :pass => 0, :dump => 0, :atboot => 0, + :ensure => :mounted}.each do + |param, value| + @mount.provider.stubs(param).returns value + @mount[param] = value + end + + @mount.provider.stubs(:mounted?).returns true + + @catalog = Puppet::Resource::Catalog.new + @catalog.add_resource @mount + end + + it "should use the provider to change the dump value" do + @mount.provider.expects(:dump).returns 0 + @mount.provider.expects(:dump=).with(1) + + @mount[:dump] = 1 + + @catalog.apply + end +end diff --git a/spec/unit/type/mount_spec_spec.rb b/spec/unit/type/mount_spec_spec.rb deleted file mode 100755 index fd9c6cb52..000000000 --- a/spec/unit/type/mount_spec_spec.rb +++ /dev/null @@ -1,211 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Type.type(:mount) do - it "should have a :refreshable feature that requires the :remount method" do - Puppet::Type.type(:mount).provider_feature(:refreshable).methods.should == [:remount] - end - - it "should have no default value for :ensure" do - mount = Puppet::Type.type(:mount).new(:name => "yay") - mount.should(:ensure).should be_nil - end -end - -describe Puppet::Type.type(:mount), "when validating attributes" do - [:name, :remounts].each do |param| - it "should have a #{param} parameter" do - Puppet::Type.type(:mount).attrtype(param).should == :param - end - end - - [:ensure, :device, :blockdevice, :fstype, :options, :pass, :dump, :atboot, :target].each do |param| - it "should have a #{param} property" do - Puppet::Type.type(:mount).attrtype(param).should == :property - end - end -end - -describe Puppet::Type.type(:mount)::Ensure, "when validating values" do - before do - @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil - Puppet::Type.type(:mount).defaultprovider.expects(:new).returns(@provider) - end - - it "should support :present as a value to :ensure" do - Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) - end - - it "should alias :unmounted to :present as a value to :ensure" do - mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :unmounted) - mount.should(:ensure).should == :present - end - - it "should support :absent as a value to :ensure" do - Puppet::Type.type(:mount).new(:name => "yay", :ensure => :absent) - end - - it "should support :mounted as a value to :ensure" do - Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) - end -end - -describe Puppet::Type.type(:mount)::Ensure do - before :each do - @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock - Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) - @mount = Puppet::Type.type(:mount).new(:name => "yay", :check => :ensure) - - @ensure = @mount.property(:ensure) - end - - def mount_stub(params) - Puppet::Type.type(:mount).validproperties.each do |prop| - unless params[prop] - params[prop] = :absent - @mount[prop] = :absent - end - end - - params.each do |param, value| - @provider.stubs(param).returns(value) - end - end - - describe Puppet::Type.type(:mount)::Ensure, "when retrieving its current state" do - - it "should return the provider's value if it is :absent" do - @provider.expects(:ensure).returns(:absent) - @ensure.retrieve.should == :absent - end - - it "should return :mounted if the provider indicates it is mounted and the value is not :absent" do - @provider.expects(:ensure).returns(:present) - @provider.expects(:mounted?).returns(true) - @ensure.retrieve.should == :mounted - end - - it "should return :present if the provider indicates it is not mounted and the value is not :absent" do - @provider.expects(:ensure).returns(:present) - @provider.expects(:mounted?).returns(false) - @ensure.retrieve.should == :present - end - end - - describe Puppet::Type.type(:mount)::Ensure, "when changing the host" do - - it "should destroy itself if it should be absent" do - @provider.stubs(:mounted?).returns(false) - @provider.expects(:destroy) - @ensure.should = :absent - @ensure.sync - end - - it "should unmount itself before destroying if it is mounted and should be absent" do - @provider.expects(:mounted?).returns(true) - @provider.expects(:unmount) - @provider.expects(:destroy) - @ensure.should = :absent - @ensure.sync - end - - it "should create itself if it is absent and should be present" do - @provider.stubs(:mounted?).returns(false) - @provider.expects(:create) - @ensure.should = :present - @ensure.sync - end - - it "should unmount itself if it is mounted and should be present" do - @provider.stubs(:mounted?).returns(true) - - # The interface here is just too much work to test right now. - @ensure.stubs(:syncothers) - @provider.expects(:unmount) - @ensure.should = :present - @ensure.sync - end - - it "should create and mount itself if it does not exist and should be mounted" do - @provider.stubs(:ensure).returns(:absent) - @provider.stubs(:mounted?).returns(false) - @provider.expects(:create) - @ensure.stubs(:syncothers) - @provider.expects(:mount) - @ensure.should = :mounted - @ensure.sync - end - - it "should mount itself if it is present and should be mounted" do - @provider.stubs(:ensure).returns(:present) - @provider.stubs(:mounted?).returns(false) - @ensure.stubs(:syncothers) - @provider.expects(:mount) - @ensure.should = :mounted - @ensure.sync - end - - it "should create but not mount itself if it is absent and mounted and should be mounted" do - @provider.stubs(:ensure).returns(:absent) - @provider.stubs(:mounted?).returns(true) - @ensure.stubs(:syncothers) - @provider.expects(:create) - @ensure.should = :mounted - @ensure.sync - end - end - - describe Puppet::Type.type(:mount), "when responding to events" do - - it "should remount if it is currently mounted" do - @provider.expects(:mounted?).returns(true) - @provider.expects(:remount) - - @mount.refresh - end - - it "should not remount if it is not currently mounted" do - @provider.expects(:mounted?).returns(false) - @provider.expects(:remount).never - - @mount.refresh - end - - it "should not remount swap filesystems" do - @mount[:fstype] = "swap" - @provider.expects(:remount).never - - @mount.refresh - end - end -end - -describe Puppet::Type.type(:mount), "when modifying an existing mount entry" do - before do - @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :remount => nil - Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) - @mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) - - {:device => "/foo/bar", :blockdevice => "/other/bar", :target => "/what/ever", :fstype => 'eh', :options => "", :pass => 0, :dump => 0, :atboot => 0, - :ensure => :mounted}.each do - |param, value| - @mount.provider.stubs(param).returns value - @mount[param] = value - end - - @mount.provider.stubs(:mounted?).returns true - - @catalog = Puppet::Resource::Catalog.new - @catalog.add_resource @mount - end - - it "should use the provider to change the dump value" do - @mount.provider.expects(:dump).returns 0 - @mount.provider.expects(:dump=).with(1) - - @mount[:dump] = 1 - - @catalog.apply - end -end diff --git a/spec/unit/type/nagios_spec.rb b/spec/unit/type/nagios_spec.rb new file mode 100755 index 000000000..2c26d3a77 --- /dev/null +++ b/spec/unit/type/nagios_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/external/nagios' + +describe "Nagios resource types" do + Nagios::Base.eachtype do |name, nagios_type| + puppet_type = Puppet::Type.type("nagios_" + name.to_s) + + it "should have a valid type for #{name}" do + puppet_type.should_not be_nil + end + + next unless puppet_type + + describe puppet_type do + it "should be defined as a Puppet resource type" do + puppet_type.should_not be_nil + end + + it "should have documentation" do + puppet_type.instance_variable_get("@doc").should_not == "" + end + + it "should have %s as its key attribute" % nagios_type.namevar do + puppet_type.key_attributes.should == [nagios_type.namevar] + end + + it "should have documentation for its %s parameter" % nagios_type.namevar do + puppet_type.attrclass(nagios_type.namevar).instance_variable_get("@doc").should_not be_nil + end + + it "should have an ensure property" do + puppet_type.should be_validproperty(:ensure) + end + + it "should have a target property" do + puppet_type.should be_validproperty(:target) + end + + it "should have documentation for its target property" do + puppet_type.attrclass(:target).instance_variable_get("@doc").should_not be_nil + end + + nagios_type.parameters.reject { |param| param == nagios_type.namevar or param.to_s =~ /^[0-9]/ }.each do |param| + it "should have a %s property" % param do + puppet_type.should be_validproperty(param) + end + + it "should have documentation for its %s property" % param do + puppet_type.attrclass(param).instance_variable_get("@doc").should_not be_nil + end + end + + nagios_type.parameters.find_all { |param| param.to_s =~ /^[0-9]/ }.each do |param| + it "should have not have a %s property" % param do + puppet_type.should_not be_validproperty(:param) + end + end + end + end +end diff --git a/spec/unit/type/nagios_spec_spec.rb b/spec/unit/type/nagios_spec_spec.rb deleted file mode 100755 index 2c26d3a77..000000000 --- a/spec/unit/type/nagios_spec_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/external/nagios' - -describe "Nagios resource types" do - Nagios::Base.eachtype do |name, nagios_type| - puppet_type = Puppet::Type.type("nagios_" + name.to_s) - - it "should have a valid type for #{name}" do - puppet_type.should_not be_nil - end - - next unless puppet_type - - describe puppet_type do - it "should be defined as a Puppet resource type" do - puppet_type.should_not be_nil - end - - it "should have documentation" do - puppet_type.instance_variable_get("@doc").should_not == "" - end - - it "should have %s as its key attribute" % nagios_type.namevar do - puppet_type.key_attributes.should == [nagios_type.namevar] - end - - it "should have documentation for its %s parameter" % nagios_type.namevar do - puppet_type.attrclass(nagios_type.namevar).instance_variable_get("@doc").should_not be_nil - end - - it "should have an ensure property" do - puppet_type.should be_validproperty(:ensure) - end - - it "should have a target property" do - puppet_type.should be_validproperty(:target) - end - - it "should have documentation for its target property" do - puppet_type.attrclass(:target).instance_variable_get("@doc").should_not be_nil - end - - nagios_type.parameters.reject { |param| param == nagios_type.namevar or param.to_s =~ /^[0-9]/ }.each do |param| - it "should have a %s property" % param do - puppet_type.should be_validproperty(param) - end - - it "should have documentation for its %s property" % param do - puppet_type.attrclass(param).instance_variable_get("@doc").should_not be_nil - end - end - - nagios_type.parameters.find_all { |param| param.to_s =~ /^[0-9]/ }.each do |param| - it "should have not have a %s property" % param do - puppet_type.should_not be_validproperty(:param) - end - end - end - end -end diff --git a/spec/unit/type/noop_metaparam_spec.rb b/spec/unit/type/noop_metaparam_spec.rb new file mode 100755 index 000000000..2c4b6dc49 --- /dev/null +++ b/spec/unit/type/noop_metaparam_spec.rb @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/type' + +describe Puppet::Type.type(:file).attrclass(:noop) do + before do + Puppet.settings.stubs(:use) + @file = Puppet::Type.newfile :path => "/what/ever" + end + + it "should accept true as a value" do + lambda { @file[:noop] = true }.should_not raise_error + end + + it "should accept false as a value" do + lambda { @file[:noop] = false }.should_not raise_error + end + + describe "when set on a resource" do + it "should default to the :noop setting" do + Puppet.settings.expects(:value).with(:noop).returns "myval" + @file.noop.should == "myval" + end + + it "should prefer true values from the attribute" do + @file[:noop] = true + @file.noop.should be_true + end + + it "should prefer false values from the attribute" do + @file[:noop] = false + @file.noop.should be_false + end + end +end diff --git a/spec/unit/type/noop_metaparam_spec_spec.rb b/spec/unit/type/noop_metaparam_spec_spec.rb deleted file mode 100755 index 2c4b6dc49..000000000 --- a/spec/unit/type/noop_metaparam_spec_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/type' - -describe Puppet::Type.type(:file).attrclass(:noop) do - before do - Puppet.settings.stubs(:use) - @file = Puppet::Type.newfile :path => "/what/ever" - end - - it "should accept true as a value" do - lambda { @file[:noop] = true }.should_not raise_error - end - - it "should accept false as a value" do - lambda { @file[:noop] = false }.should_not raise_error - end - - describe "when set on a resource" do - it "should default to the :noop setting" do - Puppet.settings.expects(:value).with(:noop).returns "myval" - @file.noop.should == "myval" - end - - it "should prefer true values from the attribute" do - @file[:noop] = true - @file.noop.should be_true - end - - it "should prefer false values from the attribute" do - @file[:noop] = false - @file.noop.should be_false - end - end -end diff --git a/spec/unit/type/package_spec.rb b/spec/unit/type/package_spec.rb new file mode 100755 index 000000000..11bd59778 --- /dev/null +++ b/spec/unit/type/package_spec.rb @@ -0,0 +1,236 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type.type(:package) do + it "should have an :installable feature that requires the :install method" do + Puppet::Type.type(:package).provider_feature(:installable).methods.should == [:install] + end + + it "should have an :uninstallable feature that requires the :uninstall method" do + Puppet::Type.type(:package).provider_feature(:uninstallable).methods.should == [:uninstall] + end + + it "should have an :upgradeable feature that requires :update and :latest methods" do + Puppet::Type.type(:package).provider_feature(:upgradeable).methods.should == [:update, :latest] + end + + it "should have a :purgeable feature that requires the :purge latest method" do + Puppet::Type.type(:package).provider_feature(:purgeable).methods.should == [:purge] + end + + it "should have a :versionable feature" do + Puppet::Type.type(:package).provider_feature(:versionable).should_not be_nil + end + + it "should default to being installed" do + pkg = Puppet::Type.type(:package).new(:name => "yay") + pkg.should(:ensure).should == :present + end +end + +describe Puppet::Type.type(:package), "when validating attributes" do + [:name, :source, :instance, :status, :adminfile, :responsefile, :configfiles, :category, :platform, :root, :vendor, :description, :allowcdrom].each do |param| + it "should have a #{param} parameter" do + Puppet::Type.type(:package).attrtype(param).should == :param + end + end + + it "should have an ensure property" do + Puppet::Type.type(:package).attrtype(:ensure).should == :property + end +end + +describe Puppet::Type.type(:package), "when validating attribute values" do + before do + @provider = stub 'provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil + Puppet::Type.type(:package).defaultprovider.expects(:new).returns(@provider) + end + + it "should support :present as a value to :ensure" do + Puppet::Type.type(:package).new(:name => "yay", :ensure => :present) + end + + it "should alias :installed to :present as a value to :ensure" do + pkg = Puppet::Type.type(:package).new(:name => "yay", :ensure => :installed) + pkg.should(:ensure).should == :present + end + + it "should support :absent as a value to :ensure" do + Puppet::Type.type(:package).new(:name => "yay", :ensure => :absent) + end + + it "should support :purged as a value to :ensure if the provider has the :purgeable feature" do + @provider.expects(:satisfies?).with([:purgeable]).returns(true) + Puppet::Type.type(:package).new(:name => "yay", :ensure => :purged) + end + + it "should not support :purged as a value to :ensure if the provider does not have the :purgeable feature" do + @provider.expects(:satisfies?).with([:purgeable]).returns(false) + proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => :purged) }.should raise_error(Puppet::Error) + end + + it "should support :latest as a value to :ensure if the provider has the :upgradeable feature" do + @provider.expects(:satisfies?).with([:upgradeable]).returns(true) + Puppet::Type.type(:package).new(:name => "yay", :ensure => :latest) + end + + it "should not support :latest as a value to :ensure if the provider does not have the :upgradeable feature" do + @provider.expects(:satisfies?).with([:upgradeable]).returns(false) + proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => :latest) }.should raise_error(Puppet::Error) + end + + it "should support version numbers as a value to :ensure if the provider has the :versionable feature" do + @provider.expects(:satisfies?).with([:versionable]).returns(true) + Puppet::Type.type(:package).new(:name => "yay", :ensure => "1.0") + end + + it "should not support version numbers as a value to :ensure if the provider does not have the :versionable feature" do + @provider.expects(:satisfies?).with([:versionable]).returns(false) + proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => "1.0") }.should raise_error(Puppet::Error) + end + + it "should accept any string as an argument to :source" do + proc { Puppet::Type.type(:package).new(:name => "yay", :source => "stuff") }.should_not raise_error(Puppet::Error) + end +end + +module PackageEvaluationTesting + def setprops(properties) + @provider.stubs(:properties).returns(properties) + end +end + +describe Puppet::Type.type(:package) do + before :each do + @provider = stub 'provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock + Puppet::Type.type(:package).defaultprovider.stubs(:new).returns(@provider) + Puppet::Type.type(:package).defaultprovider.stubs(:instances).returns([]) + @package = Puppet::Type.type(:package).new(:name => "yay") + + @catalog = Puppet::Resource::Catalog.new + @catalog.add_resource(@package) + end + + describe Puppet::Type.type(:package), "when it should be purged" do + include PackageEvaluationTesting + + before { @package[:ensure] = :purged } + + it "should do nothing if it is :purged" do + @provider.expects(:properties).returns(:ensure => :purged) + @catalog.apply + end + + [:absent, :installed, :present, :latest].each do |state| + it "should purge if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:purge) + @catalog.apply + end + end + end + + describe Puppet::Type.type(:package), "when it should be absent" do + include PackageEvaluationTesting + + before { @package[:ensure] = :absent } + + [:purged, :absent].each do |state| + it "should do nothing if it is #{state.to_s}" do + @provider.expects(:properties).returns(:ensure => state) + @catalog.apply + end + end + + [:installed, :present, :latest].each do |state| + it "should uninstall if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:uninstall) + @catalog.apply + end + end + end + + describe Puppet::Type.type(:package), "when it should be present" do + include PackageEvaluationTesting + + before { @package[:ensure] = :present } + + [:present, :latest, "1.0"].each do |state| + it "should do nothing if it is #{state.to_s}" do + @provider.expects(:properties).returns(:ensure => state) + @catalog.apply + end + end + + [:purged, :absent].each do |state| + it "should install if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:install) + @catalog.apply + end + end + end + + describe Puppet::Type.type(:package), "when it should be latest" do + include PackageEvaluationTesting + + before { @package[:ensure] = :latest } + + [:purged, :absent].each do |state| + it "should upgrade if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:update) + @catalog.apply + end + end + + it "should upgrade if the current version is not equal to the latest version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.stubs(:latest).returns("2.0") + @provider.expects(:update) + @catalog.apply + end + + it "should do nothing if it is equal to the latest version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.stubs(:latest).returns("1.0") + @provider.expects(:update).never + @catalog.apply + end + + it "should do nothing if the provider returns :present as the latest version" do + @provider.stubs(:properties).returns(:ensure => :present) + @provider.stubs(:latest).returns("1.0") + @provider.expects(:update).never + @catalog.apply + end + end + + describe Puppet::Type.type(:package), "when it should be a specific version" do + include PackageEvaluationTesting + + before { @package[:ensure] = "1.0" } + + [:purged, :absent].each do |state| + it "should install if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:install) + @catalog.apply + end + end + + it "should do nothing if the current version is equal to the desired version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.expects(:install).never + @catalog.apply + end + + it "should install if the current version is not equal to the specified version" do + @provider.stubs(:properties).returns(:ensure => "2.0") + @provider.expects(:install) + @catalog.apply + end + end +end diff --git a/spec/unit/type/package_spec_spec.rb b/spec/unit/type/package_spec_spec.rb deleted file mode 100755 index 11bd59778..000000000 --- a/spec/unit/type/package_spec_spec.rb +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Type.type(:package) do - it "should have an :installable feature that requires the :install method" do - Puppet::Type.type(:package).provider_feature(:installable).methods.should == [:install] - end - - it "should have an :uninstallable feature that requires the :uninstall method" do - Puppet::Type.type(:package).provider_feature(:uninstallable).methods.should == [:uninstall] - end - - it "should have an :upgradeable feature that requires :update and :latest methods" do - Puppet::Type.type(:package).provider_feature(:upgradeable).methods.should == [:update, :latest] - end - - it "should have a :purgeable feature that requires the :purge latest method" do - Puppet::Type.type(:package).provider_feature(:purgeable).methods.should == [:purge] - end - - it "should have a :versionable feature" do - Puppet::Type.type(:package).provider_feature(:versionable).should_not be_nil - end - - it "should default to being installed" do - pkg = Puppet::Type.type(:package).new(:name => "yay") - pkg.should(:ensure).should == :present - end -end - -describe Puppet::Type.type(:package), "when validating attributes" do - [:name, :source, :instance, :status, :adminfile, :responsefile, :configfiles, :category, :platform, :root, :vendor, :description, :allowcdrom].each do |param| - it "should have a #{param} parameter" do - Puppet::Type.type(:package).attrtype(param).should == :param - end - end - - it "should have an ensure property" do - Puppet::Type.type(:package).attrtype(:ensure).should == :property - end -end - -describe Puppet::Type.type(:package), "when validating attribute values" do - before do - @provider = stub 'provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil - Puppet::Type.type(:package).defaultprovider.expects(:new).returns(@provider) - end - - it "should support :present as a value to :ensure" do - Puppet::Type.type(:package).new(:name => "yay", :ensure => :present) - end - - it "should alias :installed to :present as a value to :ensure" do - pkg = Puppet::Type.type(:package).new(:name => "yay", :ensure => :installed) - pkg.should(:ensure).should == :present - end - - it "should support :absent as a value to :ensure" do - Puppet::Type.type(:package).new(:name => "yay", :ensure => :absent) - end - - it "should support :purged as a value to :ensure if the provider has the :purgeable feature" do - @provider.expects(:satisfies?).with([:purgeable]).returns(true) - Puppet::Type.type(:package).new(:name => "yay", :ensure => :purged) - end - - it "should not support :purged as a value to :ensure if the provider does not have the :purgeable feature" do - @provider.expects(:satisfies?).with([:purgeable]).returns(false) - proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => :purged) }.should raise_error(Puppet::Error) - end - - it "should support :latest as a value to :ensure if the provider has the :upgradeable feature" do - @provider.expects(:satisfies?).with([:upgradeable]).returns(true) - Puppet::Type.type(:package).new(:name => "yay", :ensure => :latest) - end - - it "should not support :latest as a value to :ensure if the provider does not have the :upgradeable feature" do - @provider.expects(:satisfies?).with([:upgradeable]).returns(false) - proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => :latest) }.should raise_error(Puppet::Error) - end - - it "should support version numbers as a value to :ensure if the provider has the :versionable feature" do - @provider.expects(:satisfies?).with([:versionable]).returns(true) - Puppet::Type.type(:package).new(:name => "yay", :ensure => "1.0") - end - - it "should not support version numbers as a value to :ensure if the provider does not have the :versionable feature" do - @provider.expects(:satisfies?).with([:versionable]).returns(false) - proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => "1.0") }.should raise_error(Puppet::Error) - end - - it "should accept any string as an argument to :source" do - proc { Puppet::Type.type(:package).new(:name => "yay", :source => "stuff") }.should_not raise_error(Puppet::Error) - end -end - -module PackageEvaluationTesting - def setprops(properties) - @provider.stubs(:properties).returns(properties) - end -end - -describe Puppet::Type.type(:package) do - before :each do - @provider = stub 'provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock - Puppet::Type.type(:package).defaultprovider.stubs(:new).returns(@provider) - Puppet::Type.type(:package).defaultprovider.stubs(:instances).returns([]) - @package = Puppet::Type.type(:package).new(:name => "yay") - - @catalog = Puppet::Resource::Catalog.new - @catalog.add_resource(@package) - end - - describe Puppet::Type.type(:package), "when it should be purged" do - include PackageEvaluationTesting - - before { @package[:ensure] = :purged } - - it "should do nothing if it is :purged" do - @provider.expects(:properties).returns(:ensure => :purged) - @catalog.apply - end - - [:absent, :installed, :present, :latest].each do |state| - it "should purge if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:purge) - @catalog.apply - end - end - end - - describe Puppet::Type.type(:package), "when it should be absent" do - include PackageEvaluationTesting - - before { @package[:ensure] = :absent } - - [:purged, :absent].each do |state| - it "should do nothing if it is #{state.to_s}" do - @provider.expects(:properties).returns(:ensure => state) - @catalog.apply - end - end - - [:installed, :present, :latest].each do |state| - it "should uninstall if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:uninstall) - @catalog.apply - end - end - end - - describe Puppet::Type.type(:package), "when it should be present" do - include PackageEvaluationTesting - - before { @package[:ensure] = :present } - - [:present, :latest, "1.0"].each do |state| - it "should do nothing if it is #{state.to_s}" do - @provider.expects(:properties).returns(:ensure => state) - @catalog.apply - end - end - - [:purged, :absent].each do |state| - it "should install if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:install) - @catalog.apply - end - end - end - - describe Puppet::Type.type(:package), "when it should be latest" do - include PackageEvaluationTesting - - before { @package[:ensure] = :latest } - - [:purged, :absent].each do |state| - it "should upgrade if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:update) - @catalog.apply - end - end - - it "should upgrade if the current version is not equal to the latest version" do - @provider.stubs(:properties).returns(:ensure => "1.0") - @provider.stubs(:latest).returns("2.0") - @provider.expects(:update) - @catalog.apply - end - - it "should do nothing if it is equal to the latest version" do - @provider.stubs(:properties).returns(:ensure => "1.0") - @provider.stubs(:latest).returns("1.0") - @provider.expects(:update).never - @catalog.apply - end - - it "should do nothing if the provider returns :present as the latest version" do - @provider.stubs(:properties).returns(:ensure => :present) - @provider.stubs(:latest).returns("1.0") - @provider.expects(:update).never - @catalog.apply - end - end - - describe Puppet::Type.type(:package), "when it should be a specific version" do - include PackageEvaluationTesting - - before { @package[:ensure] = "1.0" } - - [:purged, :absent].each do |state| - it "should install if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:install) - @catalog.apply - end - end - - it "should do nothing if the current version is equal to the desired version" do - @provider.stubs(:properties).returns(:ensure => "1.0") - @provider.expects(:install).never - @catalog.apply - end - - it "should install if the current version is not equal to the specified version" do - @provider.stubs(:properties).returns(:ensure => "2.0") - @provider.expects(:install) - @catalog.apply - end - end -end diff --git a/spec/unit/type/resources_spec.rb b/spec/unit/type/resources_spec.rb new file mode 100644 index 000000000..3e65aec14 --- /dev/null +++ b/spec/unit/type/resources_spec.rb @@ -0,0 +1,102 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +resources = Puppet::Type.type(:resources) + +# There are still plenty of tests to port over from test/. +describe resources do + describe "when initializing" do + it "should fail if the specified resource type does not exist" do + Puppet::Type.stubs(:type).with { |x| x.to_s.downcase == "resources"}.returns resources + Puppet::Type.expects(:type).with("nosuchtype").returns nil + lambda { resources.new :name => "nosuchtype" }.should raise_error(Puppet::Error) + end + + it "should not fail when the specified resource type exists" do + lambda { resources.new :name => "file" }.should_not raise_error + end + + it "should set its :resource_type attribute" do + resources.new(:name => "file").resource_type.should == Puppet::Type.type(:file) + end + end + + describe "#generate" do + before do + @host1 = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') + @catalog = Puppet::Resource::Catalog.new + @context = Puppet::Transaction.new(@catalog) + end + + describe "when dealing with non-purging resources" do + before do + @resources = Puppet::Type.type(:resources).new(:name => 'host') + end + + it "should not generate any resource" do + @resources.generate.should be_empty + end + end + + describe "when the catalog contains a purging resource" do + before do + @resources = Puppet::Type.type(:resources).new(:name => 'host', :purge => true) + @purgeable_resource = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') + @catalog.add_resource @resources + end + + it "should not generate a duplicate of that resource" do + Puppet::Type.type(:host).stubs(:instances).returns [@host1] + @catalog.add_resource @host1 + @resources.generate.collect { |r| r.ref }.should_not include(@host1.ref) + end + + it "should not include the skipped users" do + res = Puppet::Type.type(:resources).new :name => :user, :purge => true + res.catalog = Puppet::Resource::Catalog.new + + users = [ + Puppet::Type.type(:user).new(:name => "root") + ] + Puppet::Type.type(:user).expects(:instances).returns users + list = res.generate + + names = list.collect { |r| r[:name] } + names.should_not be_include("root") + end + + describe "when generating a purgeable resource" do + it "should be included in the generated resources" do + Puppet::Type.type(:host).stubs(:instances).returns [@purgeable_resource] + @resources.generate.collect { |r| r.ref }.should include(@purgeable_resource.ref) + end + end + + describe "when the instance's do not have an ensure property" do + it "should not be included in the generated resources" do + @no_ensure_resource = Puppet::Type.type(:exec).new(:name => '/usr/bin/env echo') + Puppet::Type.type(:host).stubs(:instances).returns [@no_ensure_resource] + @resources.generate.collect { |r| r.ref }.should_not include(@no_ensure_resource.ref) + end + end + + describe "when the instance's ensure property does not accept absent" do + it "should not be included in the generated resources" do + @no_absent_resource = Puppet::Type.type(:service).new(:name => 'foobar') + Puppet::Type.type(:host).stubs(:instances).returns [@no_absent_resource] + @resources.generate.collect { |r| r.ref }.should_not include(@no_absent_resource.ref) + end + end + + describe "when checking the instance fails" do + it "should not be included in the generated resources" do + @purgeable_resource = Puppet::Type.type(:host).new(:name => 'foobar') + Puppet::Type.type(:host).stubs(:instances).returns [@purgeable_resource] + @resources.expects(:check).with(@purgeable_resource).returns(false) + @resources.generate.collect { |r| r.ref }.should_not include(@purgeable_resource.ref) + end + end + end + end +end diff --git a/spec/unit/type/resources_spec_spec.rb b/spec/unit/type/resources_spec_spec.rb deleted file mode 100644 index 3e65aec14..000000000 --- a/spec/unit/type/resources_spec_spec.rb +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -resources = Puppet::Type.type(:resources) - -# There are still plenty of tests to port over from test/. -describe resources do - describe "when initializing" do - it "should fail if the specified resource type does not exist" do - Puppet::Type.stubs(:type).with { |x| x.to_s.downcase == "resources"}.returns resources - Puppet::Type.expects(:type).with("nosuchtype").returns nil - lambda { resources.new :name => "nosuchtype" }.should raise_error(Puppet::Error) - end - - it "should not fail when the specified resource type exists" do - lambda { resources.new :name => "file" }.should_not raise_error - end - - it "should set its :resource_type attribute" do - resources.new(:name => "file").resource_type.should == Puppet::Type.type(:file) - end - end - - describe "#generate" do - before do - @host1 = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') - @catalog = Puppet::Resource::Catalog.new - @context = Puppet::Transaction.new(@catalog) - end - - describe "when dealing with non-purging resources" do - before do - @resources = Puppet::Type.type(:resources).new(:name => 'host') - end - - it "should not generate any resource" do - @resources.generate.should be_empty - end - end - - describe "when the catalog contains a purging resource" do - before do - @resources = Puppet::Type.type(:resources).new(:name => 'host', :purge => true) - @purgeable_resource = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') - @catalog.add_resource @resources - end - - it "should not generate a duplicate of that resource" do - Puppet::Type.type(:host).stubs(:instances).returns [@host1] - @catalog.add_resource @host1 - @resources.generate.collect { |r| r.ref }.should_not include(@host1.ref) - end - - it "should not include the skipped users" do - res = Puppet::Type.type(:resources).new :name => :user, :purge => true - res.catalog = Puppet::Resource::Catalog.new - - users = [ - Puppet::Type.type(:user).new(:name => "root") - ] - Puppet::Type.type(:user).expects(:instances).returns users - list = res.generate - - names = list.collect { |r| r[:name] } - names.should_not be_include("root") - end - - describe "when generating a purgeable resource" do - it "should be included in the generated resources" do - Puppet::Type.type(:host).stubs(:instances).returns [@purgeable_resource] - @resources.generate.collect { |r| r.ref }.should include(@purgeable_resource.ref) - end - end - - describe "when the instance's do not have an ensure property" do - it "should not be included in the generated resources" do - @no_ensure_resource = Puppet::Type.type(:exec).new(:name => '/usr/bin/env echo') - Puppet::Type.type(:host).stubs(:instances).returns [@no_ensure_resource] - @resources.generate.collect { |r| r.ref }.should_not include(@no_ensure_resource.ref) - end - end - - describe "when the instance's ensure property does not accept absent" do - it "should not be included in the generated resources" do - @no_absent_resource = Puppet::Type.type(:service).new(:name => 'foobar') - Puppet::Type.type(:host).stubs(:instances).returns [@no_absent_resource] - @resources.generate.collect { |r| r.ref }.should_not include(@no_absent_resource.ref) - end - end - - describe "when checking the instance fails" do - it "should not be included in the generated resources" do - @purgeable_resource = Puppet::Type.type(:host).new(:name => 'foobar') - Puppet::Type.type(:host).stubs(:instances).returns [@purgeable_resource] - @resources.expects(:check).with(@purgeable_resource).returns(false) - @resources.generate.collect { |r| r.ref }.should_not include(@purgeable_resource.ref) - end - end - end - end -end diff --git a/spec/unit/type/schedule_spec.rb b/spec/unit/type/schedule_spec.rb new file mode 100755 index 000000000..8807d0fa0 --- /dev/null +++ b/spec/unit/type/schedule_spec.rb @@ -0,0 +1,333 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +module ScheduleTesting + + def format(time) + time.strftime("%H:%M:%S") + end + + def diff(unit, incr, method, count) + diff = Time.now.to_i.send(method, incr * count) + Time.at(diff) + end + + def month(method, count) + diff(:hour, 3600 * 24 * 30, method, count) + end + + def week(method, count) + diff(:hour, 3600 * 24 * 7, method, count) + end + + def day(method, count) + diff(:hour, 3600 * 24, method, count) + end + + def hour(method, count) + diff(:hour, 3600, method, count) + end + + def min(method, count) + diff(:min, 60, method, count) + end + + def sec(method, count) + diff(:sec, 1, method, count) + end + +end + +describe Puppet::Type.type(:schedule) do + before :each do + Puppet.settings.stubs(:value).with(:ignoreschedules).returns(false) + + @schedule = Puppet::Type.type(:schedule).new(:name => "testing") + end + + describe Puppet::Type.type(:schedule) do + include ScheduleTesting + + it "should default to :distance for period-matching" do + @schedule[:periodmatch].should == :distance + end + + it "should default to a :repeat of 1" do + @schedule[:repeat].should == 1 + end + + it "should never match when the period is :never" do + @schedule[:period] = :never + @schedule.match?.should be_false + end + end + + describe Puppet::Type.type(:schedule), "when producing default schedules" do + include ScheduleTesting + + %w{hourly daily weekly monthly never}.each do |period| + period = period.to_sym + it "should produce a #{period} schedule with the period set appropriately" do + schedules = Puppet::Type.type(:schedule).mkdefaultschedules + schedules.find { |s| s[:name] == period.to_s and s[:period] == period }.should be_instance_of(Puppet::Type.type(:schedule)) + end + end + + it "should produce a schedule named puppet with a period of hourly and a repeat of 2" do + schedules = Puppet::Type.type(:schedule).mkdefaultschedules + schedules.find { |s| + s[:name] == "puppet" and s[:period] == :hourly and s[:repeat] == 2 + }.should be_instance_of(Puppet::Type.type(:schedule)) + end + end + + describe Puppet::Type.type(:schedule), "when matching ranges" do + include ScheduleTesting + + it "should match when the start time is before the current time and the end time is after the current time" do + @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now + 10)] + @schedule.match?.should be_true + end + + it "should not match when the start time is after the current time" do + @schedule[:range] = "%s - %s" % [format(Time.now + 5), format(Time.now + 10)] + @schedule.match?.should be_false + end + + it "should not match when the end time is previous to the current time" do + @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now - 5)] + @schedule.match?.should be_false + end + end + + describe Puppet::Type.type(:schedule), "when matching hourly by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :hourly + @schedule[:periodmatch] = :distance + end + + it "should match an hour ago" do + @schedule.match?(hour("-", 1)).should be_true + end + + it "should not match now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match 59 minutes ago" do + @schedule.match?(min("-", 59)).should be_false + end + end + + describe Puppet::Type.type(:schedule), "when matching daily by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :daily + @schedule[:periodmatch] = :distance + end + + it "should match when the previous time was one day ago" do + @schedule.match?(day("-", 1)).should be_true + end + + it "should not match when the previous time is now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match when the previous time was 23 hours ago" do + @schedule.match?(hour("-", 23)).should be_false + end + end + + describe Puppet::Type.type(:schedule), "when matching weekly by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :weekly + @schedule[:periodmatch] = :distance + end + + it "should match seven days ago" do + @schedule.match?(day("-", 7)).should be_true + end + + it "should not match now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match six days ago" do + @schedule.match?(day("-", 6)).should be_false + end + end + + describe Puppet::Type.type(:schedule), "when matching monthly by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :monthly + @schedule[:periodmatch] = :distance + end + + it "should match 32 days ago" do + @schedule.match?(day("-", 32)).should be_true + end + + it "should not match now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match 27 days ago" do + @schedule.match?(day("-", 27)).should be_false + end + end + + describe Puppet::Type.type(:schedule), "when matching hourly by number" do + include ScheduleTesting + + before do + @schedule[:period] = :hourly + @schedule[:periodmatch] = :number + end + + it "should match if the times are one minute apart and the current minute is 0" do + current = Time.now + + # Subtract an hour, reset the minute to zero, then add 59 minutes, so we're the previous hour plus 59 minutes. + previous = (current - 3600 - (current.min * 60) + (59 * 60)) + + # Now set the "current" time to the zero minute of the current hour. + now = (current - (current.min * 60)) + Time.stubs(:now).returns(now) + @schedule.match?(previous).should be_true + end + + it "should not match if the times are 58 minutes apart and the current minute is 59" do + current = Time.now + + # reset the minute to zero + previous = current - (current.min * 60) + + # Now set the "current" time to the 59th minute of the current hour. + now = (current - (current.min * 60) + (59 * 60)) + Time.stubs(:now).returns(now) + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type.type(:schedule), "when matching daily by number" do + include ScheduleTesting + + before do + @schedule[:period] = :daily + @schedule[:periodmatch] = :number + end + + it "should match if the times are one minute apart and the current minute and hour are 0" do + zero = Time.now + + # Reset the current time to X:00:00 + current = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec + + # Now set the previous time to one minute before that + previous = current - 60 + + Time.stubs(:now).returns(current) + @schedule.match?(previous).should be_true + end + + it "should not match if the times are 23 hours and 58 minutes apart and the current hour is 23 and the current minute is 59" do + zero = Time.now + + # Reset the previous time to 00:00:00 + previous = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec + + # Set the current time to 23:59 + now = previous + (23 * 3600) + (59 * 60) + + Time.stubs(:now).returns(now) + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type.type(:schedule), "when matching weekly by number" do + include ScheduleTesting + + before do + @schedule[:period] = :weekly + @schedule[:periodmatch] = :number + end + + it "should match if the previous time is prior to the most recent Sunday" do + now = Time.now + + # Subtract the number days we've progressed into the week, plus one because we're zero-indexed. + previous = now - (3600 * 24 * (now.wday + 1)) + + @schedule.match?(previous).should be_true + end + + it "should not match if the previous time is after the most recent Saturday" do + now = Time.now + + # Subtract the number days we've progressed into the week + previous = now - (3600 * 24 * now.wday) + + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type.type(:schedule), "when matching monthly by number" do + include ScheduleTesting + + before do + @schedule[:period] = :monthly + @schedule[:periodmatch] = :number + end + + it "should match when the previous time is prior to the first day of this month" do + now = Time.now + + # Subtract the number days we've progressed into the month + previous = now - (3600 * 24 * now.day) + + @schedule.match?(previous).should be_true + end + + it "should not match when the previous time is after the last day of last month" do + now = Time.now + + # Subtract the number days we've progressed into the month, minus one + previous = now - (3600 * 24 * (now.day - 1)) + + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type.type(:schedule), "when matching with a repeat greater than one" do + include ScheduleTesting + + before do + @schedule[:period] = :daily + @schedule[:repeat] = 2 + end + + it "should fail if the periodmatch is 'number'" do + @schedule[:periodmatch] = :number + proc { @schedule[:repeat] = 2 }.should raise_error(Puppet::Error) + end + + it "should match if the previous run was further away than the distance divided by the repeat" do + previous = Time.now - (3600 * 13) + @schedule.match?(previous).should be_true + end + + it "should not match if the previous run was closer than the distance divided by the repeat" do + previous = Time.now - (3600 * 11) + @schedule.match?(previous).should be_false + end + end +end diff --git a/spec/unit/type/schedule_spec_spec.rb b/spec/unit/type/schedule_spec_spec.rb deleted file mode 100755 index 8807d0fa0..000000000 --- a/spec/unit/type/schedule_spec_spec.rb +++ /dev/null @@ -1,333 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -module ScheduleTesting - - def format(time) - time.strftime("%H:%M:%S") - end - - def diff(unit, incr, method, count) - diff = Time.now.to_i.send(method, incr * count) - Time.at(diff) - end - - def month(method, count) - diff(:hour, 3600 * 24 * 30, method, count) - end - - def week(method, count) - diff(:hour, 3600 * 24 * 7, method, count) - end - - def day(method, count) - diff(:hour, 3600 * 24, method, count) - end - - def hour(method, count) - diff(:hour, 3600, method, count) - end - - def min(method, count) - diff(:min, 60, method, count) - end - - def sec(method, count) - diff(:sec, 1, method, count) - end - -end - -describe Puppet::Type.type(:schedule) do - before :each do - Puppet.settings.stubs(:value).with(:ignoreschedules).returns(false) - - @schedule = Puppet::Type.type(:schedule).new(:name => "testing") - end - - describe Puppet::Type.type(:schedule) do - include ScheduleTesting - - it "should default to :distance for period-matching" do - @schedule[:periodmatch].should == :distance - end - - it "should default to a :repeat of 1" do - @schedule[:repeat].should == 1 - end - - it "should never match when the period is :never" do - @schedule[:period] = :never - @schedule.match?.should be_false - end - end - - describe Puppet::Type.type(:schedule), "when producing default schedules" do - include ScheduleTesting - - %w{hourly daily weekly monthly never}.each do |period| - period = period.to_sym - it "should produce a #{period} schedule with the period set appropriately" do - schedules = Puppet::Type.type(:schedule).mkdefaultschedules - schedules.find { |s| s[:name] == period.to_s and s[:period] == period }.should be_instance_of(Puppet::Type.type(:schedule)) - end - end - - it "should produce a schedule named puppet with a period of hourly and a repeat of 2" do - schedules = Puppet::Type.type(:schedule).mkdefaultschedules - schedules.find { |s| - s[:name] == "puppet" and s[:period] == :hourly and s[:repeat] == 2 - }.should be_instance_of(Puppet::Type.type(:schedule)) - end - end - - describe Puppet::Type.type(:schedule), "when matching ranges" do - include ScheduleTesting - - it "should match when the start time is before the current time and the end time is after the current time" do - @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now + 10)] - @schedule.match?.should be_true - end - - it "should not match when the start time is after the current time" do - @schedule[:range] = "%s - %s" % [format(Time.now + 5), format(Time.now + 10)] - @schedule.match?.should be_false - end - - it "should not match when the end time is previous to the current time" do - @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now - 5)] - @schedule.match?.should be_false - end - end - - describe Puppet::Type.type(:schedule), "when matching hourly by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :hourly - @schedule[:periodmatch] = :distance - end - - it "should match an hour ago" do - @schedule.match?(hour("-", 1)).should be_true - end - - it "should not match now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match 59 minutes ago" do - @schedule.match?(min("-", 59)).should be_false - end - end - - describe Puppet::Type.type(:schedule), "when matching daily by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :daily - @schedule[:periodmatch] = :distance - end - - it "should match when the previous time was one day ago" do - @schedule.match?(day("-", 1)).should be_true - end - - it "should not match when the previous time is now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match when the previous time was 23 hours ago" do - @schedule.match?(hour("-", 23)).should be_false - end - end - - describe Puppet::Type.type(:schedule), "when matching weekly by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :weekly - @schedule[:periodmatch] = :distance - end - - it "should match seven days ago" do - @schedule.match?(day("-", 7)).should be_true - end - - it "should not match now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match six days ago" do - @schedule.match?(day("-", 6)).should be_false - end - end - - describe Puppet::Type.type(:schedule), "when matching monthly by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :monthly - @schedule[:periodmatch] = :distance - end - - it "should match 32 days ago" do - @schedule.match?(day("-", 32)).should be_true - end - - it "should not match now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match 27 days ago" do - @schedule.match?(day("-", 27)).should be_false - end - end - - describe Puppet::Type.type(:schedule), "when matching hourly by number" do - include ScheduleTesting - - before do - @schedule[:period] = :hourly - @schedule[:periodmatch] = :number - end - - it "should match if the times are one minute apart and the current minute is 0" do - current = Time.now - - # Subtract an hour, reset the minute to zero, then add 59 minutes, so we're the previous hour plus 59 minutes. - previous = (current - 3600 - (current.min * 60) + (59 * 60)) - - # Now set the "current" time to the zero minute of the current hour. - now = (current - (current.min * 60)) - Time.stubs(:now).returns(now) - @schedule.match?(previous).should be_true - end - - it "should not match if the times are 58 minutes apart and the current minute is 59" do - current = Time.now - - # reset the minute to zero - previous = current - (current.min * 60) - - # Now set the "current" time to the 59th minute of the current hour. - now = (current - (current.min * 60) + (59 * 60)) - Time.stubs(:now).returns(now) - @schedule.match?(previous).should be_false - end - end - - describe Puppet::Type.type(:schedule), "when matching daily by number" do - include ScheduleTesting - - before do - @schedule[:period] = :daily - @schedule[:periodmatch] = :number - end - - it "should match if the times are one minute apart and the current minute and hour are 0" do - zero = Time.now - - # Reset the current time to X:00:00 - current = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec - - # Now set the previous time to one minute before that - previous = current - 60 - - Time.stubs(:now).returns(current) - @schedule.match?(previous).should be_true - end - - it "should not match if the times are 23 hours and 58 minutes apart and the current hour is 23 and the current minute is 59" do - zero = Time.now - - # Reset the previous time to 00:00:00 - previous = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec - - # Set the current time to 23:59 - now = previous + (23 * 3600) + (59 * 60) - - Time.stubs(:now).returns(now) - @schedule.match?(previous).should be_false - end - end - - describe Puppet::Type.type(:schedule), "when matching weekly by number" do - include ScheduleTesting - - before do - @schedule[:period] = :weekly - @schedule[:periodmatch] = :number - end - - it "should match if the previous time is prior to the most recent Sunday" do - now = Time.now - - # Subtract the number days we've progressed into the week, plus one because we're zero-indexed. - previous = now - (3600 * 24 * (now.wday + 1)) - - @schedule.match?(previous).should be_true - end - - it "should not match if the previous time is after the most recent Saturday" do - now = Time.now - - # Subtract the number days we've progressed into the week - previous = now - (3600 * 24 * now.wday) - - @schedule.match?(previous).should be_false - end - end - - describe Puppet::Type.type(:schedule), "when matching monthly by number" do - include ScheduleTesting - - before do - @schedule[:period] = :monthly - @schedule[:periodmatch] = :number - end - - it "should match when the previous time is prior to the first day of this month" do - now = Time.now - - # Subtract the number days we've progressed into the month - previous = now - (3600 * 24 * now.day) - - @schedule.match?(previous).should be_true - end - - it "should not match when the previous time is after the last day of last month" do - now = Time.now - - # Subtract the number days we've progressed into the month, minus one - previous = now - (3600 * 24 * (now.day - 1)) - - @schedule.match?(previous).should be_false - end - end - - describe Puppet::Type.type(:schedule), "when matching with a repeat greater than one" do - include ScheduleTesting - - before do - @schedule[:period] = :daily - @schedule[:repeat] = 2 - end - - it "should fail if the periodmatch is 'number'" do - @schedule[:periodmatch] = :number - proc { @schedule[:repeat] = 2 }.should raise_error(Puppet::Error) - end - - it "should match if the previous run was further away than the distance divided by the repeat" do - previous = Time.now - (3600 * 13) - @schedule.match?(previous).should be_true - end - - it "should not match if the previous run was closer than the distance divided by the repeat" do - previous = Time.now - (3600 * 11) - @schedule.match?(previous).should be_false - end - end -end diff --git a/spec/unit/type/selboolean_spec.rb b/spec/unit/type/selboolean_spec.rb new file mode 100755 index 000000000..7f719b46e --- /dev/null +++ b/spec/unit/type/selboolean_spec.rb @@ -0,0 +1,45 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +describe Puppet::Type.type(:selboolean), "when validating attributes" do + [:name, :persistent].each do |param| + it "should have a #{param} parameter" do + Puppet::Type.type(:selboolean).attrtype(param).should == :param + end + end + + it "should have a value property" do + Puppet::Type.type(:selboolean).attrtype(:value).should == :property + end +end + +describe Puppet::Type.type(:selboolean), "when validating values" do + before do + @class = Puppet::Type.type(:selboolean) + + @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true + @class.stubs(:defaultprovider).returns(@provider_class) + @class.stubs(:provider).returns(@provider_class) + + @provider = stub 'provider', :class => @provider_class, :clear => nil + @provider_class.stubs(:new).returns(@provider) + end + + it "should support :on as a value to :value" do + Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on) + end + + it "should support :off as a value to :value" do + Puppet::Type.type(:selboolean).new(:name => "yay", :value => :off) + end + + it "should support :true as a value to :persistent" do + Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on, :persistent => :true) + end + + it "should support :false as a value to :persistent" do + Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on, :persistent => :false) + end +end + diff --git a/spec/unit/type/selboolean_spec_spec.rb b/spec/unit/type/selboolean_spec_spec.rb deleted file mode 100755 index 7f719b46e..000000000 --- a/spec/unit/type/selboolean_spec_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -describe Puppet::Type.type(:selboolean), "when validating attributes" do - [:name, :persistent].each do |param| - it "should have a #{param} parameter" do - Puppet::Type.type(:selboolean).attrtype(param).should == :param - end - end - - it "should have a value property" do - Puppet::Type.type(:selboolean).attrtype(:value).should == :property - end -end - -describe Puppet::Type.type(:selboolean), "when validating values" do - before do - @class = Puppet::Type.type(:selboolean) - - @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true - @class.stubs(:defaultprovider).returns(@provider_class) - @class.stubs(:provider).returns(@provider_class) - - @provider = stub 'provider', :class => @provider_class, :clear => nil - @provider_class.stubs(:new).returns(@provider) - end - - it "should support :on as a value to :value" do - Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on) - end - - it "should support :off as a value to :value" do - Puppet::Type.type(:selboolean).new(:name => "yay", :value => :off) - end - - it "should support :true as a value to :persistent" do - Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on, :persistent => :true) - end - - it "should support :false as a value to :persistent" do - Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on, :persistent => :false) - end -end - diff --git a/spec/unit/type/selmodule_spec.rb b/spec/unit/type/selmodule_spec.rb new file mode 100755 index 000000000..f14bea9d3 --- /dev/null +++ b/spec/unit/type/selmodule_spec.rb @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +describe Puppet::Type.type(:selmodule), "when validating attributes" do + [:name, :selmoduledir, :selmodulepath].each do |param| + it "should have a #{param} parameter" do + Puppet::Type.type(:selmodule).attrtype(param).should == :param + end + end + + [:ensure, :syncversion].each do |param| + it "should have a #{param} property" do + Puppet::Type.type(:selmodule).attrtype(param).should == :property + end + end +end + diff --git a/spec/unit/type/selmodule_spec_spec.rb b/spec/unit/type/selmodule_spec_spec.rb deleted file mode 100755 index f14bea9d3..000000000 --- a/spec/unit/type/selmodule_spec_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -describe Puppet::Type.type(:selmodule), "when validating attributes" do - [:name, :selmoduledir, :selmodulepath].each do |param| - it "should have a #{param} parameter" do - Puppet::Type.type(:selmodule).attrtype(param).should == :param - end - end - - [:ensure, :syncversion].each do |param| - it "should have a #{param} property" do - Puppet::Type.type(:selmodule).attrtype(param).should == :property - end - end -end - diff --git a/spec/unit/type/service_spec.rb b/spec/unit/type/service_spec.rb new file mode 100755 index 000000000..48d87ba61 --- /dev/null +++ b/spec/unit/type/service_spec.rb @@ -0,0 +1,222 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type.type(:service) do + it "should have an :enableable feature that requires the :enable, :disable, and :enabled? methods" do + Puppet::Type.type(:service).provider_feature(:enableable).methods.should == [:disable, :enable, :enabled?] + end + + it "should have a :refreshable feature that requires the :restart method" do + Puppet::Type.type(:service).provider_feature(:refreshable).methods.should == [:restart] + end +end + +describe Puppet::Type.type(:service), "when validating attributes" do + [:name, :binary, :hasstatus, :path, :pattern, :start, :restart, :stop, :status, :hasrestart, :control].each do |param| + it "should have a #{param} parameter" do + Puppet::Type.type(:service).attrtype(param).should == :param + end + end + + [:ensure, :enable].each do |param| + it "should have an #{param} property" do + Puppet::Type.type(:service).attrtype(param).should == :property + end + end +end + +describe Puppet::Type.type(:service), "when validating attribute values" do + before do + @provider = stub 'provider', :class => Puppet::Type.type(:service).defaultprovider, :clear => nil, :controllable? => false + Puppet::Type.type(:service).defaultprovider.stubs(:new).returns(@provider) + end + + it "should support :running as a value to :ensure" do + Puppet::Type.type(:service).new(:name => "yay", :ensure => :running) + end + + it "should support :stopped as a value to :ensure" do + Puppet::Type.type(:service).new(:name => "yay", :ensure => :stopped) + end + + it "should alias the value :true to :running in :ensure" do + svc = Puppet::Type.type(:service).new(:name => "yay", :ensure => true) + svc.should(:ensure).should == :running + end + + it "should alias the value :false to :stopped in :ensure" do + svc = Puppet::Type.type(:service).new(:name => "yay", :ensure => false) + svc.should(:ensure).should == :stopped + end + + it "should support :true as a value to :enable" do + Puppet::Type.type(:service).new(:name => "yay", :enable => :true) + end + + it "should support :false as a value to :enable" do + Puppet::Type.type(:service).new(:name => "yay", :enable => :false) + end + + it "should support :true as a value to :hasstatus" do + Puppet::Type.type(:service).new(:name => "yay", :hasstatus => :true) + end + + it "should support :false as a value to :hasstatus" do + Puppet::Type.type(:service).new(:name => "yay", :hasstatus => :false) + end + + it "should support :true as a value to :hasrestart" do + Puppet::Type.type(:service).new(:name => "yay", :hasrestart => :true) + end + + it "should support :false as a value to :hasrestart" do + Puppet::Type.type(:service).new(:name => "yay", :hasrestart => :false) + end + + it "should allow setting the :enable parameter if the provider has the :enableable feature" do + Puppet::Type.type(:service).defaultprovider.stubs(:supports_parameter?).returns(true) + svc = Puppet::Type.type(:service).new(:name => "yay", :enable => true) + svc.should(:enable).should == :true + end + + it "should split paths on ':'" do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(true) + svc = Puppet::Type.type(:service).new(:name => "yay", :path => "/one/two:/three/four") + svc[:path].should == %w{/one/two /three/four} + end + + it "should accept arrays of paths joined by ':'" do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(true) + svc = Puppet::Type.type(:service).new(:name => "yay", :path => ["/one:/two", "/three:/four"]) + svc[:path].should == %w{/one /two /three /four} + end +end + +describe Puppet::Type.type(:service), "when setting default attribute values" do + it "should default to the provider's default path if one is available" do + FileTest.stubs(:directory?).returns(true) + FileTest.stubs(:exist?).returns(true) + + Puppet::Type.type(:service).defaultprovider.stubs(:respond_to?).returns(true) + Puppet::Type.type(:service).defaultprovider.stubs(:defpath).returns("testing") + svc = Puppet::Type.type(:service).new(:name => "other") + svc[:path].should == ["testing"] + end + + it "should default 'pattern' to the binary if one is provided" do + svc = Puppet::Type.type(:service).new(:name => "other", :binary => "/some/binary") + svc[:pattern].should == "/some/binary" + end + + it "should default 'pattern' to the name if no pattern is provided" do + svc = Puppet::Type.type(:service).new(:name => "other") + svc[:pattern].should == "other" + end + + it "should default 'control' to the upcased service name with periods replaced by underscores if the provider supports the 'controllable' feature" do + provider = stub 'provider', :controllable? => true, :class => Puppet::Type.type(:service).defaultprovider, :clear => nil + Puppet::Type.type(:service).defaultprovider.stubs(:new).returns(provider) + svc = Puppet::Type.type(:service).new(:name => "nfs.client") + svc[:control].should == "NFS_CLIENT_START" + end +end + +describe Puppet::Type.type(:service), "when retrieving the host's current state" do + before do + @service = Puppet::Type.type(:service).new(:name => "yay") + end + + it "should use the provider's status to determine whether the service is running" do + @service.provider.expects(:status).returns(:yepper) + @service[:ensure] = :running + @service.property(:ensure).retrieve.should == :yepper + end + + it "should ask the provider whether it is enabled" do + @service.provider.class.stubs(:supports_parameter?).returns(true) + @service.provider.expects(:enabled?).returns(:yepper) + @service[:enable] = true + @service.property(:enable).retrieve.should == :yepper + end +end + +describe Puppet::Type.type(:service), "when changing the host" do + before do + @service = Puppet::Type.type(:service).new(:name => "yay") + end + + it "should start the service if it is supposed to be running" do + @service[:ensure] = :running + @service.provider.expects(:start) + @service.property(:ensure).sync + end + + it "should stop the service if it is supposed to be stopped" do + @service[:ensure] = :stopped + @service.provider.expects(:stop) + @service.property(:ensure).sync + end + + it "should enable the service if it is supposed to be enabled" do + @service.provider.class.stubs(:supports_parameter?).returns(true) + @service[:enable] = true + @service.provider.expects(:enable) + @service.property(:enable).sync + end + + it "should disable the service if it is supposed to be disabled" do + @service.provider.class.stubs(:supports_parameter?).returns(true) + @service[:enable] = false + @service.provider.expects(:disable) + @service.property(:enable).sync + end + + it "should sync the service's enable state when changing the state of :ensure if :enable is being managed" do + @service.provider.class.stubs(:supports_parameter?).returns(true) + @service[:enable] = false + @service[:ensure] = :stopped + + @service.property(:enable).expects(:retrieve).returns("whatever") + @service.property(:enable).expects(:insync?).returns(false) + @service.property(:enable).expects(:sync) + + @service.provider.stubs(:stop) + + @service.property(:ensure).sync + end +end + +describe Puppet::Type.type(:service), "when refreshing the service" do + before do + @service = Puppet::Type.type(:service).new(:name => "yay") + end + + it "should restart the service if it is running" do + @service[:ensure] = :running + @service.provider.expects(:status).returns(:running) + @service.provider.expects(:restart) + @service.refresh + end + + it "should restart the service if it is running, even if it is supposed to stopped" do + @service[:ensure] = :stopped + @service.provider.expects(:status).returns(:running) + @service.provider.expects(:restart) + @service.refresh + end + + it "should not restart the service if it is not running" do + @service[:ensure] = :running + @service.provider.expects(:status).returns(:stopped) + @service.refresh + end + + it "should add :ensure as a property if it is not being managed" do + @service.provider.expects(:status).returns(:running) + @service.provider.expects(:restart) + @service.refresh + end +end diff --git a/spec/unit/type/service_spec_spec.rb b/spec/unit/type/service_spec_spec.rb deleted file mode 100755 index 48d87ba61..000000000 --- a/spec/unit/type/service_spec_spec.rb +++ /dev/null @@ -1,222 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Type.type(:service) do - it "should have an :enableable feature that requires the :enable, :disable, and :enabled? methods" do - Puppet::Type.type(:service).provider_feature(:enableable).methods.should == [:disable, :enable, :enabled?] - end - - it "should have a :refreshable feature that requires the :restart method" do - Puppet::Type.type(:service).provider_feature(:refreshable).methods.should == [:restart] - end -end - -describe Puppet::Type.type(:service), "when validating attributes" do - [:name, :binary, :hasstatus, :path, :pattern, :start, :restart, :stop, :status, :hasrestart, :control].each do |param| - it "should have a #{param} parameter" do - Puppet::Type.type(:service).attrtype(param).should == :param - end - end - - [:ensure, :enable].each do |param| - it "should have an #{param} property" do - Puppet::Type.type(:service).attrtype(param).should == :property - end - end -end - -describe Puppet::Type.type(:service), "when validating attribute values" do - before do - @provider = stub 'provider', :class => Puppet::Type.type(:service).defaultprovider, :clear => nil, :controllable? => false - Puppet::Type.type(:service).defaultprovider.stubs(:new).returns(@provider) - end - - it "should support :running as a value to :ensure" do - Puppet::Type.type(:service).new(:name => "yay", :ensure => :running) - end - - it "should support :stopped as a value to :ensure" do - Puppet::Type.type(:service).new(:name => "yay", :ensure => :stopped) - end - - it "should alias the value :true to :running in :ensure" do - svc = Puppet::Type.type(:service).new(:name => "yay", :ensure => true) - svc.should(:ensure).should == :running - end - - it "should alias the value :false to :stopped in :ensure" do - svc = Puppet::Type.type(:service).new(:name => "yay", :ensure => false) - svc.should(:ensure).should == :stopped - end - - it "should support :true as a value to :enable" do - Puppet::Type.type(:service).new(:name => "yay", :enable => :true) - end - - it "should support :false as a value to :enable" do - Puppet::Type.type(:service).new(:name => "yay", :enable => :false) - end - - it "should support :true as a value to :hasstatus" do - Puppet::Type.type(:service).new(:name => "yay", :hasstatus => :true) - end - - it "should support :false as a value to :hasstatus" do - Puppet::Type.type(:service).new(:name => "yay", :hasstatus => :false) - end - - it "should support :true as a value to :hasrestart" do - Puppet::Type.type(:service).new(:name => "yay", :hasrestart => :true) - end - - it "should support :false as a value to :hasrestart" do - Puppet::Type.type(:service).new(:name => "yay", :hasrestart => :false) - end - - it "should allow setting the :enable parameter if the provider has the :enableable feature" do - Puppet::Type.type(:service).defaultprovider.stubs(:supports_parameter?).returns(true) - svc = Puppet::Type.type(:service).new(:name => "yay", :enable => true) - svc.should(:enable).should == :true - end - - it "should split paths on ':'" do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(true) - svc = Puppet::Type.type(:service).new(:name => "yay", :path => "/one/two:/three/four") - svc[:path].should == %w{/one/two /three/four} - end - - it "should accept arrays of paths joined by ':'" do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(true) - svc = Puppet::Type.type(:service).new(:name => "yay", :path => ["/one:/two", "/three:/four"]) - svc[:path].should == %w{/one /two /three /four} - end -end - -describe Puppet::Type.type(:service), "when setting default attribute values" do - it "should default to the provider's default path if one is available" do - FileTest.stubs(:directory?).returns(true) - FileTest.stubs(:exist?).returns(true) - - Puppet::Type.type(:service).defaultprovider.stubs(:respond_to?).returns(true) - Puppet::Type.type(:service).defaultprovider.stubs(:defpath).returns("testing") - svc = Puppet::Type.type(:service).new(:name => "other") - svc[:path].should == ["testing"] - end - - it "should default 'pattern' to the binary if one is provided" do - svc = Puppet::Type.type(:service).new(:name => "other", :binary => "/some/binary") - svc[:pattern].should == "/some/binary" - end - - it "should default 'pattern' to the name if no pattern is provided" do - svc = Puppet::Type.type(:service).new(:name => "other") - svc[:pattern].should == "other" - end - - it "should default 'control' to the upcased service name with periods replaced by underscores if the provider supports the 'controllable' feature" do - provider = stub 'provider', :controllable? => true, :class => Puppet::Type.type(:service).defaultprovider, :clear => nil - Puppet::Type.type(:service).defaultprovider.stubs(:new).returns(provider) - svc = Puppet::Type.type(:service).new(:name => "nfs.client") - svc[:control].should == "NFS_CLIENT_START" - end -end - -describe Puppet::Type.type(:service), "when retrieving the host's current state" do - before do - @service = Puppet::Type.type(:service).new(:name => "yay") - end - - it "should use the provider's status to determine whether the service is running" do - @service.provider.expects(:status).returns(:yepper) - @service[:ensure] = :running - @service.property(:ensure).retrieve.should == :yepper - end - - it "should ask the provider whether it is enabled" do - @service.provider.class.stubs(:supports_parameter?).returns(true) - @service.provider.expects(:enabled?).returns(:yepper) - @service[:enable] = true - @service.property(:enable).retrieve.should == :yepper - end -end - -describe Puppet::Type.type(:service), "when changing the host" do - before do - @service = Puppet::Type.type(:service).new(:name => "yay") - end - - it "should start the service if it is supposed to be running" do - @service[:ensure] = :running - @service.provider.expects(:start) - @service.property(:ensure).sync - end - - it "should stop the service if it is supposed to be stopped" do - @service[:ensure] = :stopped - @service.provider.expects(:stop) - @service.property(:ensure).sync - end - - it "should enable the service if it is supposed to be enabled" do - @service.provider.class.stubs(:supports_parameter?).returns(true) - @service[:enable] = true - @service.provider.expects(:enable) - @service.property(:enable).sync - end - - it "should disable the service if it is supposed to be disabled" do - @service.provider.class.stubs(:supports_parameter?).returns(true) - @service[:enable] = false - @service.provider.expects(:disable) - @service.property(:enable).sync - end - - it "should sync the service's enable state when changing the state of :ensure if :enable is being managed" do - @service.provider.class.stubs(:supports_parameter?).returns(true) - @service[:enable] = false - @service[:ensure] = :stopped - - @service.property(:enable).expects(:retrieve).returns("whatever") - @service.property(:enable).expects(:insync?).returns(false) - @service.property(:enable).expects(:sync) - - @service.provider.stubs(:stop) - - @service.property(:ensure).sync - end -end - -describe Puppet::Type.type(:service), "when refreshing the service" do - before do - @service = Puppet::Type.type(:service).new(:name => "yay") - end - - it "should restart the service if it is running" do - @service[:ensure] = :running - @service.provider.expects(:status).returns(:running) - @service.provider.expects(:restart) - @service.refresh - end - - it "should restart the service if it is running, even if it is supposed to stopped" do - @service[:ensure] = :stopped - @service.provider.expects(:status).returns(:running) - @service.provider.expects(:restart) - @service.refresh - end - - it "should not restart the service if it is not running" do - @service[:ensure] = :running - @service.provider.expects(:status).returns(:stopped) - @service.refresh - end - - it "should add :ensure as a property if it is not being managed" do - @service.provider.expects(:status).returns(:running) - @service.provider.expects(:restart) - @service.refresh - end -end diff --git a/spec/unit/type/ssh_authorized_key_spec.rb b/spec/unit/type/ssh_authorized_key_spec.rb new file mode 100755 index 000000000..2fc34d318 --- /dev/null +++ b/spec/unit/type/ssh_authorized_key_spec.rb @@ -0,0 +1,140 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +ssh_authorized_key = Puppet::Type.type(:ssh_authorized_key) + +describe ssh_authorized_key do + before do + @class = Puppet::Type.type(:ssh_authorized_key) + + @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true + @class.stubs(:defaultprovider).returns(@provider_class) + @class.stubs(:provider).returns(@provider_class) + + @provider = stub 'provider', :class => @provider_class, :file_path => "/tmp/whatever", :clear => nil + @provider_class.stubs(:new).returns(@provider) + @catalog = Puppet::Resource::Catalog.new + end + + it "should have a name parameter" do + @class.attrtype(:name).should == :param + end + + it "should have :name be its namevar" do + @class.key_attributes.should == [:name] + end + + it "should have a :provider parameter" do + @class.attrtype(:provider).should == :param + end + + it "should have an ensure property" do + @class.attrtype(:ensure).should == :property + end + + it "should support :present as a value for :ensure" do + proc { @class.new(:name => "whev", :ensure => :present, :user => "nobody") }.should_not raise_error + end + + it "should support :absent as a value for :ensure" do + proc { @class.new(:name => "whev", :ensure => :absent, :user => "nobody") }.should_not raise_error + end + + it "should have an type property" do + @class.attrtype(:type).should == :property + end + it "should support ssh-dss as an type value" do + proc { @class.new(:name => "whev", :type => "ssh-dss", :user => "nobody") }.should_not raise_error + end + it "should support ssh-rsa as an type value" do + proc { @class.new(:name => "whev", :type => "ssh-rsa", :user => "nobody") }.should_not raise_error + end + it "should support :dsa as an type value" do + proc { @class.new(:name => "whev", :type => :dsa, :user => "nobody") }.should_not raise_error + end + it "should support :rsa as an type value" do + proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody") }.should_not raise_error + end + + it "should not support values other than ssh-dss, ssh-rsa, dsa, rsa in the ssh_authorized_key_type" do + proc { @class.new(:name => "whev", :type => :something) }.should raise_error(Puppet::Error) + end + + it "should have an key property" do + @class.attrtype(:key).should == :property + end + + it "should have an user property" do + @class.attrtype(:user).should == :property + end + + it "should have an options property" do + @class.attrtype(:options).should == :property + end + + it "'s options property should return well formed string of arrays from is_to_s" do + resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) + + resource.property(:options).is_to_s(["a","b","c"]).should == "a,b,c" + end + + it "'s options property should return well formed string of arrays from is_to_s" do + resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) + + resource.property(:options).should_to_s(["a","b","c"]).should == "a,b,c" + end + + it "should have a target property" do + @class.attrtype(:target).should == :property + end + + describe "when neither user nor target is specified" do + it "should raise an error" do + proc do + @class.create( + :name => "Test", + :key => "AAA", + :type => "ssh-rsa", + :ensure => :present) + end.should raise_error(Puppet::Error) + end + end + + describe "when both target and user are specified" do + it "should use target" do + resource = @class.create( + :name => "Test", + :user => "root", + :target => "/tmp/blah") + resource.should(:target).should == "/tmp/blah" + end + end + + + describe "when user is specified" do + it "should determine target" do + resource = @class.create( + :name => "Test", + :user => "root") + target = File.expand_path("~root/.ssh/authorized_keys") + resource.should(:target).should == target + end + + # Bug #2124 - ssh_authorized_key always changes target if target is not defined + it "should not raise spurious change events" do + resource = @class.new(:name => "Test", :user => "root") + target = File.expand_path("~root/.ssh/authorized_keys") + resource.property(:target).insync?(target).should == true + end + end + + describe "when calling validate" do + it "should not crash on a non-existant user" do + resource = @class.create( + :name => "Test", + :user => "ihopesuchuserdoesnotexist") + proc { resource.validate }.should_not raise_error + end + end +end diff --git a/spec/unit/type/ssh_authorized_key_spec_spec.rb b/spec/unit/type/ssh_authorized_key_spec_spec.rb deleted file mode 100755 index 2fc34d318..000000000 --- a/spec/unit/type/ssh_authorized_key_spec_spec.rb +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -ssh_authorized_key = Puppet::Type.type(:ssh_authorized_key) - -describe ssh_authorized_key do - before do - @class = Puppet::Type.type(:ssh_authorized_key) - - @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true - @class.stubs(:defaultprovider).returns(@provider_class) - @class.stubs(:provider).returns(@provider_class) - - @provider = stub 'provider', :class => @provider_class, :file_path => "/tmp/whatever", :clear => nil - @provider_class.stubs(:new).returns(@provider) - @catalog = Puppet::Resource::Catalog.new - end - - it "should have a name parameter" do - @class.attrtype(:name).should == :param - end - - it "should have :name be its namevar" do - @class.key_attributes.should == [:name] - end - - it "should have a :provider parameter" do - @class.attrtype(:provider).should == :param - end - - it "should have an ensure property" do - @class.attrtype(:ensure).should == :property - end - - it "should support :present as a value for :ensure" do - proc { @class.new(:name => "whev", :ensure => :present, :user => "nobody") }.should_not raise_error - end - - it "should support :absent as a value for :ensure" do - proc { @class.new(:name => "whev", :ensure => :absent, :user => "nobody") }.should_not raise_error - end - - it "should have an type property" do - @class.attrtype(:type).should == :property - end - it "should support ssh-dss as an type value" do - proc { @class.new(:name => "whev", :type => "ssh-dss", :user => "nobody") }.should_not raise_error - end - it "should support ssh-rsa as an type value" do - proc { @class.new(:name => "whev", :type => "ssh-rsa", :user => "nobody") }.should_not raise_error - end - it "should support :dsa as an type value" do - proc { @class.new(:name => "whev", :type => :dsa, :user => "nobody") }.should_not raise_error - end - it "should support :rsa as an type value" do - proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody") }.should_not raise_error - end - - it "should not support values other than ssh-dss, ssh-rsa, dsa, rsa in the ssh_authorized_key_type" do - proc { @class.new(:name => "whev", :type => :something) }.should raise_error(Puppet::Error) - end - - it "should have an key property" do - @class.attrtype(:key).should == :property - end - - it "should have an user property" do - @class.attrtype(:user).should == :property - end - - it "should have an options property" do - @class.attrtype(:options).should == :property - end - - it "'s options property should return well formed string of arrays from is_to_s" do - resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) - - resource.property(:options).is_to_s(["a","b","c"]).should == "a,b,c" - end - - it "'s options property should return well formed string of arrays from is_to_s" do - resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) - - resource.property(:options).should_to_s(["a","b","c"]).should == "a,b,c" - end - - it "should have a target property" do - @class.attrtype(:target).should == :property - end - - describe "when neither user nor target is specified" do - it "should raise an error" do - proc do - @class.create( - :name => "Test", - :key => "AAA", - :type => "ssh-rsa", - :ensure => :present) - end.should raise_error(Puppet::Error) - end - end - - describe "when both target and user are specified" do - it "should use target" do - resource = @class.create( - :name => "Test", - :user => "root", - :target => "/tmp/blah") - resource.should(:target).should == "/tmp/blah" - end - end - - - describe "when user is specified" do - it "should determine target" do - resource = @class.create( - :name => "Test", - :user => "root") - target = File.expand_path("~root/.ssh/authorized_keys") - resource.should(:target).should == target - end - - # Bug #2124 - ssh_authorized_key always changes target if target is not defined - it "should not raise spurious change events" do - resource = @class.new(:name => "Test", :user => "root") - target = File.expand_path("~root/.ssh/authorized_keys") - resource.property(:target).insync?(target).should == true - end - end - - describe "when calling validate" do - it "should not crash on a non-existant user" do - resource = @class.create( - :name => "Test", - :user => "ihopesuchuserdoesnotexist") - proc { resource.validate }.should_not raise_error - end - end -end diff --git a/spec/unit/type/stage_spec.rb b/spec/unit/type/stage_spec.rb new file mode 100644 index 000000000..68846520d --- /dev/null +++ b/spec/unit/type/stage_spec.rb @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type.type(:stage) do + it "should have a 'name' parameter'" do + Puppet::Type.type(:stage).new(:name => :foo)[:name].should == :foo + end +end diff --git a/spec/unit/type/stage_spec_spec.rb b/spec/unit/type/stage_spec_spec.rb deleted file mode 100644 index 68846520d..000000000 --- a/spec/unit/type/stage_spec_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Type.type(:stage) do - it "should have a 'name' parameter'" do - Puppet::Type.type(:stage).new(:name => :foo)[:name].should == :foo - end -end diff --git a/spec/unit/type/tidy_spec.rb b/spec/unit/type/tidy_spec.rb new file mode 100755 index 000000000..1b2f49798 --- /dev/null +++ b/spec/unit/type/tidy_spec.rb @@ -0,0 +1,424 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/file_bucket/dipper' + +tidy = Puppet::Type.type(:tidy) + +describe tidy do + before do + @basepath = Puppet.features.posix? ? "/what/ever" : "C:/tmp" + Puppet.settings.stubs(:use) + + # for an unknown reason some of these specs fails when run individually + # with a failed expectation on File.lstat in the autoloader. + File.stubs(:lstat) + end + + it "should use :lstat when stating a file" do + resource = tidy.new :path => "/foo/bar", :age => "1d" + stat = mock 'stat' + File.expects(:lstat).with("/foo/bar").returns stat + resource.stat("/foo/bar").should == stat + end + + [:age, :size, :path, :matches, :type, :recurse, :rmdirs].each do |param| + it "should have a %s parameter" % param do + Puppet::Type.type(:tidy).attrclass(param).ancestors.should be_include(Puppet::Parameter) + end + + it "should have documentation for its %s param" % param do + Puppet::Type.type(:tidy).attrclass(param).doc.should be_instance_of(String) + end + end + + describe "when validating parameter values" do + describe "for 'recurse'" do + before do + @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" + end + + it "should allow 'true'" do + lambda { @tidy[:recurse] = true }.should_not raise_error + end + + it "should allow 'false'" do + lambda { @tidy[:recurse] = false }.should_not raise_error + end + + it "should allow integers" do + lambda { @tidy[:recurse] = 10 }.should_not raise_error + end + + it "should allow string representations of integers" do + lambda { @tidy[:recurse] = "10" }.should_not raise_error + end + + it "should allow 'inf'" do + lambda { @tidy[:recurse] = "inf" }.should_not raise_error + end + + it "should not allow arbitrary values" do + lambda { @tidy[:recurse] = "whatever" }.should raise_error + end + end + + describe "for 'matches'" do + before do + @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" + end + + it "should object if matches is given with recurse is not specified" do + lambda { @tidy[:matches] = '*.doh' }.should raise_error + end + it "should object if matches is given and recurse is 0" do + lambda { @tidy[:recurse] = 0; @tidy[:matches] = '*.doh' }.should raise_error + end + it "should object if matches is given and recurse is false" do + lambda { @tidy[:recurse] = false; @tidy[:matches] = '*.doh' }.should raise_error + end + it "should not object if matches is given and recurse is > 0" do + lambda { @tidy[:recurse] = 1; @tidy[:matches] = '*.doh' }.should_not raise_error + end + it "should not object if matches is given and recurse is true" do + lambda { @tidy[:recurse] = true; @tidy[:matches] = '*.doh' }.should_not raise_error + end + end + end + + describe "when matching files by age" do + convertors = { + :second => 1, + :minute => 60 + } + + convertors[:hour] = convertors[:minute] * 60 + convertors[:day] = convertors[:hour] * 24 + convertors[:week] = convertors[:day] * 7 + + convertors.each do |unit, multiple| + it "should consider a %s to be %s seconds" % [unit, multiple] do + @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :age => "5%s" % unit.to_s[0..0] + + @tidy[:age].should == 5 * multiple + end + end + end + + describe "when matching files by size" do + convertors = { + :b => 0, + :kb => 1, + :mb => 2, + :gb => 3 + } + + convertors.each do |unit, multiple| + it "should consider a %s to be 1024^%s bytes" % [unit, multiple] do + @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :size => "5%s" % unit + + total = 5 + multiple.times { total *= 1024 } + @tidy[:size].should == total + end + end + end + + describe "when tidying" do + before do + @tidy = Puppet::Type.type(:tidy).new :path => @basepath + @stat = stub 'stat', :ftype => "directory" + File.stubs(:lstat).with(@basepath).returns @stat + end + + describe "and generating files" do + it "should set the backup on the file if backup is set on the tidy instance" do + @tidy[:backup] = "whatever" + Puppet::Type.type(:file).expects(:new).with { |args| args[:backup] == "whatever" } + + @tidy.mkfile(@basepath) + end + + it "should set the file's path to the tidy's path" do + Puppet::Type.type(:file).expects(:new).with { |args| args[:path] == @basepath } + + @tidy.mkfile(@basepath) + end + + it "should configure the file for deletion" do + Puppet::Type.type(:file).expects(:new).with { |args| args[:ensure] == :absent } + + @tidy.mkfile(@basepath) + end + + it "should force deletion on the file" do + Puppet::Type.type(:file).expects(:new).with { |args| args[:force] == true } + + @tidy.mkfile(@basepath) + end + + it "should do nothing if the targeted file does not exist" do + File.expects(:lstat).with(@basepath).raises Errno::ENOENT + + @tidy.generate.should == [] + end + end + + describe "and recursion is not used" do + it "should generate a file resource if the file should be tidied" do + @tidy.expects(:tidy?).with(@basepath).returns true + file = Puppet::Type.type(:file).new(:path => @basepath+"/eh") + @tidy.expects(:mkfile).with(@basepath).returns file + + @tidy.generate.should == [file] + end + + it "should do nothing if the file should not be tidied" do + @tidy.expects(:tidy?).with(@basepath).returns false + @tidy.expects(:mkfile).never + + @tidy.generate.should == [] + end + end + + describe "and recursion is used" do + before do + @tidy[:recurse] = true + Puppet::FileServing::Fileset.any_instance.stubs(:stat).returns mock("stat") + @fileset = Puppet::FileServing::Fileset.new(@basepath) + Puppet::FileServing::Fileset.stubs(:new).returns @fileset + end + + it "should use a Fileset for infinite recursion" do + Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true).returns @fileset + @fileset.expects(:files).returns %w{. one two} + @tidy.stubs(:tidy?).returns false + + @tidy.generate + end + + it "should use a Fileset for limited recursion" do + @tidy[:recurse] = 42 + Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true, :recurselimit => 42).returns @fileset + @fileset.expects(:files).returns %w{. one two} + @tidy.stubs(:tidy?).returns false + + @tidy.generate + end + + it "should generate a file resource for every file that should be tidied but not for files that should not be tidied" do + @fileset.expects(:files).returns %w{. one two} + + @tidy.expects(:tidy?).with(@basepath).returns true + @tidy.expects(:tidy?).with(@basepath+"/one").returns true + @tidy.expects(:tidy?).with(@basepath+"/two").returns false + + file = Puppet::Type.type(:file).new(:path => @basepath+"/eh") + @tidy.expects(:mkfile).with(@basepath).returns file + @tidy.expects(:mkfile).with(@basepath+"/one").returns file + + @tidy.generate + end + end + + describe "and determining whether a file matches provided glob patterns" do + before do + @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :recurse => 1 + @tidy[:matches] = %w{*foo* *bar*} + + @stat = mock 'stat' + + @matcher = @tidy.parameter(:matches) + end + + it "should always convert the globs to an array" do + @matcher.value = "*foo*" + @matcher.value.should == %w{*foo*} + end + + it "should return true if any pattern matches the last part of the file" do + @matcher.value = %w{*foo* *bar*} + @matcher.must be_tidy("/file/yaybarness", @stat) + end + + it "should return false if no pattern matches the last part of the file" do + @matcher.value = %w{*foo* *bar*} + @matcher.should_not be_tidy("/file/yayness", @stat) + end + end + + describe "and determining whether a file is too old" do + before do + @tidy = Puppet::Type.type(:tidy).new :path => @basepath + @stat = stub 'stat' + + @tidy[:age] = "1s" + @tidy[:type] = "mtime" + @ager = @tidy.parameter(:age) + end + + it "should use the age type specified" do + @tidy[:type] = :ctime + @stat.expects(:ctime).returns(Time.now) + + @ager.tidy?(@basepath, @stat) + end + + it "should return false if the file is more recent than the specified age" do + @stat.expects(:mtime).returns(Time.now) + + @ager.should_not be_tidy(@basepath, @stat) + end + + it "should return true if the file is older than the specified age" do + @stat.expects(:mtime).returns(Time.now - 10) + + @ager.must be_tidy(@basepath, @stat) + end + end + + describe "and determining whether a file is too large" do + before do + @tidy = Puppet::Type.type(:tidy).new :path => @basepath + @stat = stub 'stat', :ftype => "file" + + @tidy[:size] = "1kb" + @sizer = @tidy.parameter(:size) + end + + it "should return false if the file is smaller than the specified size" do + @stat.expects(:size).returns(4) # smaller than a kilobyte + + @sizer.should_not be_tidy(@basepath, @stat) + end + + it "should return true if the file is larger than the specified size" do + @stat.expects(:size).returns(1500) # larger than a kilobyte + + @sizer.must be_tidy(@basepath, @stat) + end + + it "should return true if the file is equal to the specified size" do + @stat.expects(:size).returns(1024) + + @sizer.must be_tidy(@basepath, @stat) + end + end + + describe "and determining whether a file should be tidied" do + before do + @tidy = Puppet::Type.type(:tidy).new :path => @basepath + @stat = stub 'stat', :ftype => "file" + File.stubs(:lstat).with(@basepath).returns @stat + end + + it "should not try to recurse if the file does not exist" do + @tidy[:recurse] = true + + File.stubs(:lstat).with(@basepath).returns nil + + @tidy.generate.should == [] + end + + it "should not be tidied if the file does not exist" do + File.expects(:lstat).with(@basepath).raises Errno::ENOENT + + @tidy.should_not be_tidy(@basepath) + end + + it "should not be tidied if the user has no access to the file" do + File.expects(:lstat).with(@basepath).raises Errno::EACCES + + @tidy.should_not be_tidy(@basepath) + end + + it "should not be tidied if it is a directory and rmdirs is set to false" do + stat = mock 'stat', :ftype => "directory" + File.expects(:lstat).with(@basepath).returns stat + + @tidy.should_not be_tidy(@basepath) + end + + it "should return false if it does not match any provided globs" do + @tidy[:recurse] = 1 + @tidy[:matches] = "globs" + + matches = @tidy.parameter(:matches) + matches.expects(:tidy?).with(@basepath, @stat).returns false + @tidy.should_not be_tidy(@basepath) + end + + it "should return false if it does not match aging requirements" do + @tidy[:age] = "1d" + + ager = @tidy.parameter(:age) + ager.expects(:tidy?).with(@basepath, @stat).returns false + @tidy.should_not be_tidy(@basepath) + end + + it "should return false if it does not match size requirements" do + @tidy[:size] = "1b" + + sizer = @tidy.parameter(:size) + sizer.expects(:tidy?).with(@basepath, @stat).returns false + @tidy.should_not be_tidy(@basepath) + end + + it "should tidy a file if age and size are set but only size matches" do + @tidy[:size] = "1b" + @tidy[:age] = "1d" + + @tidy.parameter(:size).stubs(:tidy?).returns true + @tidy.parameter(:age).stubs(:tidy?).returns false + @tidy.should be_tidy(@basepath) + end + + it "should tidy a file if age and size are set but only age matches" do + @tidy[:size] = "1b" + @tidy[:age] = "1d" + + @tidy.parameter(:size).stubs(:tidy?).returns false + @tidy.parameter(:age).stubs(:tidy?).returns true + @tidy.should be_tidy(@basepath) + end + + it "should tidy all files if neither age nor size is set" do + @tidy.must be_tidy(@basepath) + end + + it "should sort the results inversely by path length, so files are added to the catalog before their directories" do + @tidy[:recurse] = true + @tidy[:rmdirs] = true + fileset = Puppet::FileServing::Fileset.new(@basepath) + Puppet::FileServing::Fileset.expects(:new).returns fileset + fileset.expects(:files).returns %w{. one one/two} + + @tidy.stubs(:tidy?).returns true + + @tidy.generate.collect { |r| r[:path] }.should == [@basepath+"/one/two", @basepath+"/one", @basepath] + end + end + + it "should configure directories to require their contained files if rmdirs is enabled, so the files will be deleted first" do + @tidy[:recurse] = true + @tidy[:rmdirs] = true + fileset = mock 'fileset' + Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true).returns fileset + fileset.expects(:files).returns %w{. one two one/subone two/subtwo one/subone/ssone} + @tidy.stubs(:tidy?).returns true + + result = @tidy.generate.inject({}) { |hash, res| hash[res[:path]] = res; hash } + { + @basepath => [ @basepath+"/one", @basepath+"/two" ], + @basepath+"/one" => [@basepath+"/one/subone"], + @basepath+"/two" => [@basepath+"/two/subtwo"], + @basepath+"/one/subone" => [@basepath+"/one/subone/ssone"] + }.each do |parent, children| + children.each do |child| + ref = Puppet::Resource.new(:file, child) + result[parent][:require].find { |req| req.to_s == ref.to_s }.should_not be_nil + end + end + end + end +end diff --git a/spec/unit/type/tidy_spec_spec.rb b/spec/unit/type/tidy_spec_spec.rb deleted file mode 100755 index 1b2f49798..000000000 --- a/spec/unit/type/tidy_spec_spec.rb +++ /dev/null @@ -1,424 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/file_bucket/dipper' - -tidy = Puppet::Type.type(:tidy) - -describe tidy do - before do - @basepath = Puppet.features.posix? ? "/what/ever" : "C:/tmp" - Puppet.settings.stubs(:use) - - # for an unknown reason some of these specs fails when run individually - # with a failed expectation on File.lstat in the autoloader. - File.stubs(:lstat) - end - - it "should use :lstat when stating a file" do - resource = tidy.new :path => "/foo/bar", :age => "1d" - stat = mock 'stat' - File.expects(:lstat).with("/foo/bar").returns stat - resource.stat("/foo/bar").should == stat - end - - [:age, :size, :path, :matches, :type, :recurse, :rmdirs].each do |param| - it "should have a %s parameter" % param do - Puppet::Type.type(:tidy).attrclass(param).ancestors.should be_include(Puppet::Parameter) - end - - it "should have documentation for its %s param" % param do - Puppet::Type.type(:tidy).attrclass(param).doc.should be_instance_of(String) - end - end - - describe "when validating parameter values" do - describe "for 'recurse'" do - before do - @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" - end - - it "should allow 'true'" do - lambda { @tidy[:recurse] = true }.should_not raise_error - end - - it "should allow 'false'" do - lambda { @tidy[:recurse] = false }.should_not raise_error - end - - it "should allow integers" do - lambda { @tidy[:recurse] = 10 }.should_not raise_error - end - - it "should allow string representations of integers" do - lambda { @tidy[:recurse] = "10" }.should_not raise_error - end - - it "should allow 'inf'" do - lambda { @tidy[:recurse] = "inf" }.should_not raise_error - end - - it "should not allow arbitrary values" do - lambda { @tidy[:recurse] = "whatever" }.should raise_error - end - end - - describe "for 'matches'" do - before do - @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" - end - - it "should object if matches is given with recurse is not specified" do - lambda { @tidy[:matches] = '*.doh' }.should raise_error - end - it "should object if matches is given and recurse is 0" do - lambda { @tidy[:recurse] = 0; @tidy[:matches] = '*.doh' }.should raise_error - end - it "should object if matches is given and recurse is false" do - lambda { @tidy[:recurse] = false; @tidy[:matches] = '*.doh' }.should raise_error - end - it "should not object if matches is given and recurse is > 0" do - lambda { @tidy[:recurse] = 1; @tidy[:matches] = '*.doh' }.should_not raise_error - end - it "should not object if matches is given and recurse is true" do - lambda { @tidy[:recurse] = true; @tidy[:matches] = '*.doh' }.should_not raise_error - end - end - end - - describe "when matching files by age" do - convertors = { - :second => 1, - :minute => 60 - } - - convertors[:hour] = convertors[:minute] * 60 - convertors[:day] = convertors[:hour] * 24 - convertors[:week] = convertors[:day] * 7 - - convertors.each do |unit, multiple| - it "should consider a %s to be %s seconds" % [unit, multiple] do - @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :age => "5%s" % unit.to_s[0..0] - - @tidy[:age].should == 5 * multiple - end - end - end - - describe "when matching files by size" do - convertors = { - :b => 0, - :kb => 1, - :mb => 2, - :gb => 3 - } - - convertors.each do |unit, multiple| - it "should consider a %s to be 1024^%s bytes" % [unit, multiple] do - @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :size => "5%s" % unit - - total = 5 - multiple.times { total *= 1024 } - @tidy[:size].should == total - end - end - end - - describe "when tidying" do - before do - @tidy = Puppet::Type.type(:tidy).new :path => @basepath - @stat = stub 'stat', :ftype => "directory" - File.stubs(:lstat).with(@basepath).returns @stat - end - - describe "and generating files" do - it "should set the backup on the file if backup is set on the tidy instance" do - @tidy[:backup] = "whatever" - Puppet::Type.type(:file).expects(:new).with { |args| args[:backup] == "whatever" } - - @tidy.mkfile(@basepath) - end - - it "should set the file's path to the tidy's path" do - Puppet::Type.type(:file).expects(:new).with { |args| args[:path] == @basepath } - - @tidy.mkfile(@basepath) - end - - it "should configure the file for deletion" do - Puppet::Type.type(:file).expects(:new).with { |args| args[:ensure] == :absent } - - @tidy.mkfile(@basepath) - end - - it "should force deletion on the file" do - Puppet::Type.type(:file).expects(:new).with { |args| args[:force] == true } - - @tidy.mkfile(@basepath) - end - - it "should do nothing if the targeted file does not exist" do - File.expects(:lstat).with(@basepath).raises Errno::ENOENT - - @tidy.generate.should == [] - end - end - - describe "and recursion is not used" do - it "should generate a file resource if the file should be tidied" do - @tidy.expects(:tidy?).with(@basepath).returns true - file = Puppet::Type.type(:file).new(:path => @basepath+"/eh") - @tidy.expects(:mkfile).with(@basepath).returns file - - @tidy.generate.should == [file] - end - - it "should do nothing if the file should not be tidied" do - @tidy.expects(:tidy?).with(@basepath).returns false - @tidy.expects(:mkfile).never - - @tidy.generate.should == [] - end - end - - describe "and recursion is used" do - before do - @tidy[:recurse] = true - Puppet::FileServing::Fileset.any_instance.stubs(:stat).returns mock("stat") - @fileset = Puppet::FileServing::Fileset.new(@basepath) - Puppet::FileServing::Fileset.stubs(:new).returns @fileset - end - - it "should use a Fileset for infinite recursion" do - Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true).returns @fileset - @fileset.expects(:files).returns %w{. one two} - @tidy.stubs(:tidy?).returns false - - @tidy.generate - end - - it "should use a Fileset for limited recursion" do - @tidy[:recurse] = 42 - Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true, :recurselimit => 42).returns @fileset - @fileset.expects(:files).returns %w{. one two} - @tidy.stubs(:tidy?).returns false - - @tidy.generate - end - - it "should generate a file resource for every file that should be tidied but not for files that should not be tidied" do - @fileset.expects(:files).returns %w{. one two} - - @tidy.expects(:tidy?).with(@basepath).returns true - @tidy.expects(:tidy?).with(@basepath+"/one").returns true - @tidy.expects(:tidy?).with(@basepath+"/two").returns false - - file = Puppet::Type.type(:file).new(:path => @basepath+"/eh") - @tidy.expects(:mkfile).with(@basepath).returns file - @tidy.expects(:mkfile).with(@basepath+"/one").returns file - - @tidy.generate - end - end - - describe "and determining whether a file matches provided glob patterns" do - before do - @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :recurse => 1 - @tidy[:matches] = %w{*foo* *bar*} - - @stat = mock 'stat' - - @matcher = @tidy.parameter(:matches) - end - - it "should always convert the globs to an array" do - @matcher.value = "*foo*" - @matcher.value.should == %w{*foo*} - end - - it "should return true if any pattern matches the last part of the file" do - @matcher.value = %w{*foo* *bar*} - @matcher.must be_tidy("/file/yaybarness", @stat) - end - - it "should return false if no pattern matches the last part of the file" do - @matcher.value = %w{*foo* *bar*} - @matcher.should_not be_tidy("/file/yayness", @stat) - end - end - - describe "and determining whether a file is too old" do - before do - @tidy = Puppet::Type.type(:tidy).new :path => @basepath - @stat = stub 'stat' - - @tidy[:age] = "1s" - @tidy[:type] = "mtime" - @ager = @tidy.parameter(:age) - end - - it "should use the age type specified" do - @tidy[:type] = :ctime - @stat.expects(:ctime).returns(Time.now) - - @ager.tidy?(@basepath, @stat) - end - - it "should return false if the file is more recent than the specified age" do - @stat.expects(:mtime).returns(Time.now) - - @ager.should_not be_tidy(@basepath, @stat) - end - - it "should return true if the file is older than the specified age" do - @stat.expects(:mtime).returns(Time.now - 10) - - @ager.must be_tidy(@basepath, @stat) - end - end - - describe "and determining whether a file is too large" do - before do - @tidy = Puppet::Type.type(:tidy).new :path => @basepath - @stat = stub 'stat', :ftype => "file" - - @tidy[:size] = "1kb" - @sizer = @tidy.parameter(:size) - end - - it "should return false if the file is smaller than the specified size" do - @stat.expects(:size).returns(4) # smaller than a kilobyte - - @sizer.should_not be_tidy(@basepath, @stat) - end - - it "should return true if the file is larger than the specified size" do - @stat.expects(:size).returns(1500) # larger than a kilobyte - - @sizer.must be_tidy(@basepath, @stat) - end - - it "should return true if the file is equal to the specified size" do - @stat.expects(:size).returns(1024) - - @sizer.must be_tidy(@basepath, @stat) - end - end - - describe "and determining whether a file should be tidied" do - before do - @tidy = Puppet::Type.type(:tidy).new :path => @basepath - @stat = stub 'stat', :ftype => "file" - File.stubs(:lstat).with(@basepath).returns @stat - end - - it "should not try to recurse if the file does not exist" do - @tidy[:recurse] = true - - File.stubs(:lstat).with(@basepath).returns nil - - @tidy.generate.should == [] - end - - it "should not be tidied if the file does not exist" do - File.expects(:lstat).with(@basepath).raises Errno::ENOENT - - @tidy.should_not be_tidy(@basepath) - end - - it "should not be tidied if the user has no access to the file" do - File.expects(:lstat).with(@basepath).raises Errno::EACCES - - @tidy.should_not be_tidy(@basepath) - end - - it "should not be tidied if it is a directory and rmdirs is set to false" do - stat = mock 'stat', :ftype => "directory" - File.expects(:lstat).with(@basepath).returns stat - - @tidy.should_not be_tidy(@basepath) - end - - it "should return false if it does not match any provided globs" do - @tidy[:recurse] = 1 - @tidy[:matches] = "globs" - - matches = @tidy.parameter(:matches) - matches.expects(:tidy?).with(@basepath, @stat).returns false - @tidy.should_not be_tidy(@basepath) - end - - it "should return false if it does not match aging requirements" do - @tidy[:age] = "1d" - - ager = @tidy.parameter(:age) - ager.expects(:tidy?).with(@basepath, @stat).returns false - @tidy.should_not be_tidy(@basepath) - end - - it "should return false if it does not match size requirements" do - @tidy[:size] = "1b" - - sizer = @tidy.parameter(:size) - sizer.expects(:tidy?).with(@basepath, @stat).returns false - @tidy.should_not be_tidy(@basepath) - end - - it "should tidy a file if age and size are set but only size matches" do - @tidy[:size] = "1b" - @tidy[:age] = "1d" - - @tidy.parameter(:size).stubs(:tidy?).returns true - @tidy.parameter(:age).stubs(:tidy?).returns false - @tidy.should be_tidy(@basepath) - end - - it "should tidy a file if age and size are set but only age matches" do - @tidy[:size] = "1b" - @tidy[:age] = "1d" - - @tidy.parameter(:size).stubs(:tidy?).returns false - @tidy.parameter(:age).stubs(:tidy?).returns true - @tidy.should be_tidy(@basepath) - end - - it "should tidy all files if neither age nor size is set" do - @tidy.must be_tidy(@basepath) - end - - it "should sort the results inversely by path length, so files are added to the catalog before their directories" do - @tidy[:recurse] = true - @tidy[:rmdirs] = true - fileset = Puppet::FileServing::Fileset.new(@basepath) - Puppet::FileServing::Fileset.expects(:new).returns fileset - fileset.expects(:files).returns %w{. one one/two} - - @tidy.stubs(:tidy?).returns true - - @tidy.generate.collect { |r| r[:path] }.should == [@basepath+"/one/two", @basepath+"/one", @basepath] - end - end - - it "should configure directories to require their contained files if rmdirs is enabled, so the files will be deleted first" do - @tidy[:recurse] = true - @tidy[:rmdirs] = true - fileset = mock 'fileset' - Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true).returns fileset - fileset.expects(:files).returns %w{. one two one/subone two/subtwo one/subone/ssone} - @tidy.stubs(:tidy?).returns true - - result = @tidy.generate.inject({}) { |hash, res| hash[res[:path]] = res; hash } - { - @basepath => [ @basepath+"/one", @basepath+"/two" ], - @basepath+"/one" => [@basepath+"/one/subone"], - @basepath+"/two" => [@basepath+"/two/subtwo"], - @basepath+"/one/subone" => [@basepath+"/one/subone/ssone"] - }.each do |parent, children| - children.each do |child| - ref = Puppet::Resource.new(:file, child) - result[parent][:require].find { |req| req.to_s == ref.to_s }.should_not be_nil - end - end - end - end -end diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb new file mode 100755 index 000000000..590b35a94 --- /dev/null +++ b/spec/unit/type/user_spec.rb @@ -0,0 +1,283 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +user = Puppet::Type.type(:user) + +describe user do + before do + unless ENV["PATH"].split(File::PATH_SEPARATOR).include?("/usr/sbin") + ENV["PATH"] += File::PATH_SEPARATOR + "/usr/sbin" + end + @provider = stub 'provider' + @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil + end + + it "should have a default provider inheriting from Puppet::Provider" do + user.defaultprovider.ancestors.should be_include(Puppet::Provider) + end + + it "should be able to create a instance" do + user.new(:name => "foo").should_not be_nil + end + + it "should have an allows_duplicates feature" do + user.provider_feature(:allows_duplicates).should_not be_nil + end + + it "should have an manages_homedir feature" do + user.provider_feature(:manages_homedir).should_not be_nil + end + + it "should have an manages_passwords feature" do + user.provider_feature(:manages_passwords).should_not be_nil + end + + it "should have a manages_solaris_rbac feature" do + user.provider_feature(:manages_solaris_rbac).should_not be_nil + end + + describe "instances" do + it "should have a valid provider" do + user.new(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) + end + + it "should delegate existence questions to its provider" do + instance = user.new(:name => "foo") + instance.provider.expects(:exists?).returns "eh" + instance.exists?.should == "eh" + end + end + + properties = [:ensure, :uid, :gid, :home, :comment, :shell, :password, :groups, :roles, :auths, :profiles, :project, :keys] + + properties.each do |property| + it "should have a %s property" % property do + user.attrclass(property).ancestors.should be_include(Puppet::Property) + end + + it "should have documentation for its %s property" % property do + user.attrclass(property).doc.should be_instance_of(String) + end + end + + list_properties = [:groups, :roles, :auths] + + list_properties.each do |property| + it "should have a list '%s'" % property do + user.attrclass(property).ancestors.should be_include(Puppet::Property::List) + end + end + + it "should have an ordered list 'profiles'" do + user.attrclass(:profiles).ancestors.should be_include(Puppet::Property::OrderedList) + end + + it "should have key values 'keys'" do + user.attrclass(:keys).ancestors.should be_include(Puppet::Property::KeyValue) + end + + describe "when retrieving all current values" do + before do + @user = user.new(:name => "foo", :uid => 10) + end + + it "should return a hash containing values for all set properties" do + @user[:gid] = 10 + @user.property(:ensure).expects(:retrieve).returns :present + @user.property(:uid).expects(:retrieve).returns 15 + @user.property(:gid).expects(:retrieve).returns 15 + values = @user.retrieve + [@user.property(:uid), @user.property(:gid)].each { |property| values.should be_include(property) } + end + + it "should set all values to :absent if the user is absent" do + @user.property(:ensure).expects(:retrieve).returns :absent + @user.property(:uid).expects(:retrieve).never + @user.retrieve[@user.property(:uid)].should == :absent + end + + it "should include the result of retrieving each property's current value if the user is present" do + @user.property(:ensure).expects(:retrieve).returns :present + @user.property(:uid).expects(:retrieve).returns 15 + @user.retrieve[@user.property(:uid)].should == 15 + end + end + + describe "when managing the ensure property" do + before do + @ensure = user.attrclass(:ensure).new(:resource => @resource) + end + + it "should support a :present value" do + lambda { @ensure.should = :present }.should_not raise_error + end + + it "should support an :absent value" do + lambda { @ensure.should = :absent }.should_not raise_error + end + + it "should call :create on the provider when asked to sync to the :present state" do + @provider.expects(:create) + @ensure.should = :present + @ensure.sync + end + + it "should call :delete on the provider when asked to sync to the :absent state" do + @provider.expects(:delete) + @ensure.should = :absent + @ensure.sync + end + + describe "and determining the current state" do + it "should return :present when the provider indicates the user exists" do + @provider.expects(:exists?).returns true + @ensure.retrieve.should == :present + end + + it "should return :absent when the provider indicates the user does not exist" do + @provider.expects(:exists?).returns false + @ensure.retrieve.should == :absent + end + end + end + + describe "when managing the uid property" do + it "should convert number-looking strings into actual numbers" do + uid = user.attrclass(:uid).new(:resource => @resource) + uid.should = "50" + uid.should.must == 50 + end + + it "should support UIDs as numbers" do + uid = user.attrclass(:uid).new(:resource => @resource) + uid.should = 50 + uid.should.must == 50 + end + + it "should :absent as a value" do + uid = user.attrclass(:uid).new(:resource => @resource) + uid.should = :absent + uid.should.must == :absent + end + end + + describe "when managing the gid" do + it "should :absent as a value" do + gid = user.attrclass(:gid).new(:resource => @resource) + gid.should = :absent + gid.should.must == :absent + end + + it "should convert number-looking strings into actual numbers" do + gid = user.attrclass(:gid).new(:resource => @resource) + gid.should = "50" + gid.should.must == 50 + end + + it "should support GIDs specified as integers" do + gid = user.attrclass(:gid).new(:resource => @resource) + gid.should = 50 + gid.should.must == 50 + end + + it "should support groups specified by name" do + gid = user.attrclass(:gid).new(:resource => @resource) + gid.should = "foo" + gid.should.must == "foo" + end + + describe "when testing whether in sync" do + before do + @gid = user.attrclass(:gid).new(:resource => @resource, :should => %w{foo bar}) + end + + it "should return true if no 'should' values are set" do + @gid = user.attrclass(:gid).new(:resource => @resource) + + @gid.must be_insync(500) + end + + it "should return true if any of the specified groups are equal to the current integer" do + Puppet::Util.expects(:gid).with("foo").returns 300 + Puppet::Util.expects(:gid).with("bar").returns 500 + + @gid.must be_insync(500) + end + + it "should return false if none of the specified groups are equal to the current integer" do + Puppet::Util.expects(:gid).with("foo").returns 300 + Puppet::Util.expects(:gid).with("bar").returns 500 + + @gid.should_not be_insync(700) + end + end + + describe "when syncing" do + before do + @gid = user.attrclass(:gid).new(:resource => @resource, :should => %w{foo bar}) + end + + it "should use the first found, specified group as the desired value and send it to the provider" do + Puppet::Util.expects(:gid).with("foo").returns nil + Puppet::Util.expects(:gid).with("bar").returns 500 + + @provider.expects(:gid=).with 500 + + @gid.sync + end + end + end + + describe "when managing passwords" do + before do + @password = user.attrclass(:password).new(:resource => @resource, :should => "mypass") + end + + it "should not include the password in the change log when adding the password" do + @password.change_to_s(:absent, "mypass").should_not be_include("mypass") + end + + it "should not include the password in the change log when changing the password" do + @password.change_to_s("other", "mypass").should_not be_include("mypass") + end + + it "should fail if a ':' is included in the password" do + lambda { @password.should = "some:thing" }.should raise_error(Puppet::Error) + end + + it "should allow the value to be set to :absent" do + lambda { @password.should = :absent }.should_not raise_error + end + end + + describe "when manages_solaris_rbac is enabled" do + before do + @provider.stubs(:satisfies?).returns(false) + @provider.expects(:satisfies?).with([:manages_solaris_rbac]).returns(true) + end + + it "should support a :role value for ensure" do + @ensure = user.attrclass(:ensure).new(:resource => @resource) + lambda { @ensure.should = :role }.should_not raise_error + end + end + + describe "when user has roles" do + it "should autorequire roles" do + testuser = Puppet::Type.type(:user).new(:name => "testuser") + testuser[:roles] = "testrole" + + testrole = Puppet::Type.type(:user).new(:name => "testrole") + + config = Puppet::Resource::Catalog.new :testing do |conf| + [testuser, testrole].each { |resource| conf.add_resource resource } + end + Puppet::Type::User::ProviderDirectoryservice.stubs(:get_macosx_version_major).returns "10.5" + + rel = testuser.autorequire[0] + rel.source.ref.should == testrole.ref + rel.target.ref.should == testuser.ref + end + end +end diff --git a/spec/unit/type/user_spec_spec.rb b/spec/unit/type/user_spec_spec.rb deleted file mode 100755 index 590b35a94..000000000 --- a/spec/unit/type/user_spec_spec.rb +++ /dev/null @@ -1,283 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -user = Puppet::Type.type(:user) - -describe user do - before do - unless ENV["PATH"].split(File::PATH_SEPARATOR).include?("/usr/sbin") - ENV["PATH"] += File::PATH_SEPARATOR + "/usr/sbin" - end - @provider = stub 'provider' - @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil - end - - it "should have a default provider inheriting from Puppet::Provider" do - user.defaultprovider.ancestors.should be_include(Puppet::Provider) - end - - it "should be able to create a instance" do - user.new(:name => "foo").should_not be_nil - end - - it "should have an allows_duplicates feature" do - user.provider_feature(:allows_duplicates).should_not be_nil - end - - it "should have an manages_homedir feature" do - user.provider_feature(:manages_homedir).should_not be_nil - end - - it "should have an manages_passwords feature" do - user.provider_feature(:manages_passwords).should_not be_nil - end - - it "should have a manages_solaris_rbac feature" do - user.provider_feature(:manages_solaris_rbac).should_not be_nil - end - - describe "instances" do - it "should have a valid provider" do - user.new(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) - end - - it "should delegate existence questions to its provider" do - instance = user.new(:name => "foo") - instance.provider.expects(:exists?).returns "eh" - instance.exists?.should == "eh" - end - end - - properties = [:ensure, :uid, :gid, :home, :comment, :shell, :password, :groups, :roles, :auths, :profiles, :project, :keys] - - properties.each do |property| - it "should have a %s property" % property do - user.attrclass(property).ancestors.should be_include(Puppet::Property) - end - - it "should have documentation for its %s property" % property do - user.attrclass(property).doc.should be_instance_of(String) - end - end - - list_properties = [:groups, :roles, :auths] - - list_properties.each do |property| - it "should have a list '%s'" % property do - user.attrclass(property).ancestors.should be_include(Puppet::Property::List) - end - end - - it "should have an ordered list 'profiles'" do - user.attrclass(:profiles).ancestors.should be_include(Puppet::Property::OrderedList) - end - - it "should have key values 'keys'" do - user.attrclass(:keys).ancestors.should be_include(Puppet::Property::KeyValue) - end - - describe "when retrieving all current values" do - before do - @user = user.new(:name => "foo", :uid => 10) - end - - it "should return a hash containing values for all set properties" do - @user[:gid] = 10 - @user.property(:ensure).expects(:retrieve).returns :present - @user.property(:uid).expects(:retrieve).returns 15 - @user.property(:gid).expects(:retrieve).returns 15 - values = @user.retrieve - [@user.property(:uid), @user.property(:gid)].each { |property| values.should be_include(property) } - end - - it "should set all values to :absent if the user is absent" do - @user.property(:ensure).expects(:retrieve).returns :absent - @user.property(:uid).expects(:retrieve).never - @user.retrieve[@user.property(:uid)].should == :absent - end - - it "should include the result of retrieving each property's current value if the user is present" do - @user.property(:ensure).expects(:retrieve).returns :present - @user.property(:uid).expects(:retrieve).returns 15 - @user.retrieve[@user.property(:uid)].should == 15 - end - end - - describe "when managing the ensure property" do - before do - @ensure = user.attrclass(:ensure).new(:resource => @resource) - end - - it "should support a :present value" do - lambda { @ensure.should = :present }.should_not raise_error - end - - it "should support an :absent value" do - lambda { @ensure.should = :absent }.should_not raise_error - end - - it "should call :create on the provider when asked to sync to the :present state" do - @provider.expects(:create) - @ensure.should = :present - @ensure.sync - end - - it "should call :delete on the provider when asked to sync to the :absent state" do - @provider.expects(:delete) - @ensure.should = :absent - @ensure.sync - end - - describe "and determining the current state" do - it "should return :present when the provider indicates the user exists" do - @provider.expects(:exists?).returns true - @ensure.retrieve.should == :present - end - - it "should return :absent when the provider indicates the user does not exist" do - @provider.expects(:exists?).returns false - @ensure.retrieve.should == :absent - end - end - end - - describe "when managing the uid property" do - it "should convert number-looking strings into actual numbers" do - uid = user.attrclass(:uid).new(:resource => @resource) - uid.should = "50" - uid.should.must == 50 - end - - it "should support UIDs as numbers" do - uid = user.attrclass(:uid).new(:resource => @resource) - uid.should = 50 - uid.should.must == 50 - end - - it "should :absent as a value" do - uid = user.attrclass(:uid).new(:resource => @resource) - uid.should = :absent - uid.should.must == :absent - end - end - - describe "when managing the gid" do - it "should :absent as a value" do - gid = user.attrclass(:gid).new(:resource => @resource) - gid.should = :absent - gid.should.must == :absent - end - - it "should convert number-looking strings into actual numbers" do - gid = user.attrclass(:gid).new(:resource => @resource) - gid.should = "50" - gid.should.must == 50 - end - - it "should support GIDs specified as integers" do - gid = user.attrclass(:gid).new(:resource => @resource) - gid.should = 50 - gid.should.must == 50 - end - - it "should support groups specified by name" do - gid = user.attrclass(:gid).new(:resource => @resource) - gid.should = "foo" - gid.should.must == "foo" - end - - describe "when testing whether in sync" do - before do - @gid = user.attrclass(:gid).new(:resource => @resource, :should => %w{foo bar}) - end - - it "should return true if no 'should' values are set" do - @gid = user.attrclass(:gid).new(:resource => @resource) - - @gid.must be_insync(500) - end - - it "should return true if any of the specified groups are equal to the current integer" do - Puppet::Util.expects(:gid).with("foo").returns 300 - Puppet::Util.expects(:gid).with("bar").returns 500 - - @gid.must be_insync(500) - end - - it "should return false if none of the specified groups are equal to the current integer" do - Puppet::Util.expects(:gid).with("foo").returns 300 - Puppet::Util.expects(:gid).with("bar").returns 500 - - @gid.should_not be_insync(700) - end - end - - describe "when syncing" do - before do - @gid = user.attrclass(:gid).new(:resource => @resource, :should => %w{foo bar}) - end - - it "should use the first found, specified group as the desired value and send it to the provider" do - Puppet::Util.expects(:gid).with("foo").returns nil - Puppet::Util.expects(:gid).with("bar").returns 500 - - @provider.expects(:gid=).with 500 - - @gid.sync - end - end - end - - describe "when managing passwords" do - before do - @password = user.attrclass(:password).new(:resource => @resource, :should => "mypass") - end - - it "should not include the password in the change log when adding the password" do - @password.change_to_s(:absent, "mypass").should_not be_include("mypass") - end - - it "should not include the password in the change log when changing the password" do - @password.change_to_s("other", "mypass").should_not be_include("mypass") - end - - it "should fail if a ':' is included in the password" do - lambda { @password.should = "some:thing" }.should raise_error(Puppet::Error) - end - - it "should allow the value to be set to :absent" do - lambda { @password.should = :absent }.should_not raise_error - end - end - - describe "when manages_solaris_rbac is enabled" do - before do - @provider.stubs(:satisfies?).returns(false) - @provider.expects(:satisfies?).with([:manages_solaris_rbac]).returns(true) - end - - it "should support a :role value for ensure" do - @ensure = user.attrclass(:ensure).new(:resource => @resource) - lambda { @ensure.should = :role }.should_not raise_error - end - end - - describe "when user has roles" do - it "should autorequire roles" do - testuser = Puppet::Type.type(:user).new(:name => "testuser") - testuser[:roles] = "testrole" - - testrole = Puppet::Type.type(:user).new(:name => "testrole") - - config = Puppet::Resource::Catalog.new :testing do |conf| - [testuser, testrole].each { |resource| conf.add_resource resource } - end - Puppet::Type::User::ProviderDirectoryservice.stubs(:get_macosx_version_major).returns "10.5" - - rel = testuser.autorequire[0] - rel.source.ref.should == testrole.ref - rel.target.ref.should == testuser.ref - end - end -end diff --git a/spec/unit/type/zfs_spec.rb b/spec/unit/type/zfs_spec.rb new file mode 100755 index 000000000..e1af6821a --- /dev/null +++ b/spec/unit/type/zfs_spec.rb @@ -0,0 +1,45 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +zfs = Puppet::Type.type(:zfs) + +describe zfs do + properties = [:ensure, :mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir] + + properties.each do |property| + it "should have a %s property" % property do + zfs.attrclass(property).ancestors.should be_include(Puppet::Property) + end + end + + parameters = [:name] + + parameters.each do |parameter| + it "should have a %s parameter" % parameter do + zfs.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) + end + end + + it "should autorequire the containing zfss and the zpool" do + provider = mock "provider" + provider.stubs(:name).returns(:solaris) + zfs.stubs(:defaultprovider).returns(provider) + Puppet::Type.type(:zpool).stubs(:defaultprovider).returns(provider) + + + foo_pool = Puppet::Type.type(:zpool).new(:name => "foo") + + foo_bar_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar") + foo_bar_baz_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar/baz") + foo_bar_baz_buz_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar/baz/buz") + + config = Puppet::Resource::Catalog.new :testing do |conf| + [foo_pool, foo_bar_zfs, foo_bar_baz_zfs, foo_bar_baz_buz_zfs].each { |resource| conf.add_resource resource } + end + + req = foo_bar_baz_buz_zfs.autorequire.collect { |edge| edge.source.ref } + + [foo_pool.ref, foo_bar_zfs.ref, foo_bar_baz_zfs.ref].each { |ref| req.include?(ref).should == true } + end +end diff --git a/spec/unit/type/zfs_spec_spec.rb b/spec/unit/type/zfs_spec_spec.rb deleted file mode 100755 index e1af6821a..000000000 --- a/spec/unit/type/zfs_spec_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -zfs = Puppet::Type.type(:zfs) - -describe zfs do - properties = [:ensure, :mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir] - - properties.each do |property| - it "should have a %s property" % property do - zfs.attrclass(property).ancestors.should be_include(Puppet::Property) - end - end - - parameters = [:name] - - parameters.each do |parameter| - it "should have a %s parameter" % parameter do - zfs.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) - end - end - - it "should autorequire the containing zfss and the zpool" do - provider = mock "provider" - provider.stubs(:name).returns(:solaris) - zfs.stubs(:defaultprovider).returns(provider) - Puppet::Type.type(:zpool).stubs(:defaultprovider).returns(provider) - - - foo_pool = Puppet::Type.type(:zpool).new(:name => "foo") - - foo_bar_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar") - foo_bar_baz_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar/baz") - foo_bar_baz_buz_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar/baz/buz") - - config = Puppet::Resource::Catalog.new :testing do |conf| - [foo_pool, foo_bar_zfs, foo_bar_baz_zfs, foo_bar_baz_buz_zfs].each { |resource| conf.add_resource resource } - end - - req = foo_bar_baz_buz_zfs.autorequire.collect { |edge| edge.source.ref } - - [foo_pool.ref, foo_bar_zfs.ref, foo_bar_baz_zfs.ref].each { |ref| req.include?(ref).should == true } - end -end diff --git a/spec/unit/type/zone_spec.rb b/spec/unit/type/zone_spec.rb new file mode 100755 index 000000000..679141ee9 --- /dev/null +++ b/spec/unit/type/zone_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +zone = Puppet::Type.type(:zone) + +describe zone do + before do + zone = Puppet::Type.type(:zone) + provider = stub 'provider' + provider.stubs(:name).returns(:solaris) + zone.stubs(:defaultprovider).returns(provider) + resource = stub 'resource', :resource => nil, :provider => provider, :line => nil, :file => nil + end + + parameters = [:create_args, :install_args, :sysidcfg, :path, :realhostname] + + parameters.each do |parameter| + it "should have a %s parameter" % parameter do + zone.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) + end + end + + properties = [:ip, :iptype, :autoboot, :pool, :shares, :inherit] + + properties.each do |property| + it "should have a %s property" % property do + zone.attrclass(property).ancestors.should be_include(Puppet::Property) + end + end + + it "should be invalid when :path is missing" do + lambda { zone.new(:name => "dummy") }.should raise_error + end + + it "should be invalid when :ip is missing a \":\" and iptype is :shared" do + lambda { zone.new(:name => "dummy", :ip => "if") }.should raise_error + end + + it "should be invalid when :ip has a \":\" and iptype is :exclusive" do + lambda { zone.new(:name => "dummy", :ip => "if:1.2.3.4", + :iptype => :exclusive) }.should raise_error + end + + it "should be invalid when :ip has two \":\" and iptype is :exclusive" do + lambda { zone.new(:name => "dummy", :ip => "if:1.2.3.4:2.3.4.5", + :iptype => :exclusive) }.should raise_error + end + + it "should be valid when :iptype is :shared and using interface and ip" do + zone.new(:name => "dummy", :path => "/dummy", :ip => "if:1.2.3.4") + end + + it "should be valid when :iptype is :shared and using interface, ip and default route" do + zone.new(:name => "dummy", :path => "/dummy", :ip => "if:1.2.3.4:2.3.4.5") + end + + it "should be valid when :iptype is :exclusive and using interface" do + zone.new(:name => "dummy", :path => "/dummy", :ip => "if", + :iptype => :exclusive) + end + +end diff --git a/spec/unit/type/zone_spec_spec.rb b/spec/unit/type/zone_spec_spec.rb deleted file mode 100755 index 679141ee9..000000000 --- a/spec/unit/type/zone_spec_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -zone = Puppet::Type.type(:zone) - -describe zone do - before do - zone = Puppet::Type.type(:zone) - provider = stub 'provider' - provider.stubs(:name).returns(:solaris) - zone.stubs(:defaultprovider).returns(provider) - resource = stub 'resource', :resource => nil, :provider => provider, :line => nil, :file => nil - end - - parameters = [:create_args, :install_args, :sysidcfg, :path, :realhostname] - - parameters.each do |parameter| - it "should have a %s parameter" % parameter do - zone.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) - end - end - - properties = [:ip, :iptype, :autoboot, :pool, :shares, :inherit] - - properties.each do |property| - it "should have a %s property" % property do - zone.attrclass(property).ancestors.should be_include(Puppet::Property) - end - end - - it "should be invalid when :path is missing" do - lambda { zone.new(:name => "dummy") }.should raise_error - end - - it "should be invalid when :ip is missing a \":\" and iptype is :shared" do - lambda { zone.new(:name => "dummy", :ip => "if") }.should raise_error - end - - it "should be invalid when :ip has a \":\" and iptype is :exclusive" do - lambda { zone.new(:name => "dummy", :ip => "if:1.2.3.4", - :iptype => :exclusive) }.should raise_error - end - - it "should be invalid when :ip has two \":\" and iptype is :exclusive" do - lambda { zone.new(:name => "dummy", :ip => "if:1.2.3.4:2.3.4.5", - :iptype => :exclusive) }.should raise_error - end - - it "should be valid when :iptype is :shared and using interface and ip" do - zone.new(:name => "dummy", :path => "/dummy", :ip => "if:1.2.3.4") - end - - it "should be valid when :iptype is :shared and using interface, ip and default route" do - zone.new(:name => "dummy", :path => "/dummy", :ip => "if:1.2.3.4:2.3.4.5") - end - - it "should be valid when :iptype is :exclusive and using interface" do - zone.new(:name => "dummy", :path => "/dummy", :ip => "if", - :iptype => :exclusive) - end - -end diff --git a/spec/unit/type/zpool_spec.rb b/spec/unit/type/zpool_spec.rb new file mode 100755 index 000000000..c957e5684 --- /dev/null +++ b/spec/unit/type/zpool_spec.rb @@ -0,0 +1,110 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +zpool = Puppet::Type.type(:zpool) + +describe zpool do + before do + @provider = stub 'provider' + @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil + end + + properties = [:ensure, :disk, :mirror, :raidz, :spare, :log] + + properties.each do |property| + it "should have a %s property" % property do + zpool.attrclass(property).ancestors.should be_include(Puppet::Property) + end + end + + parameters = [:pool, :raid_parity] + + parameters.each do |parameter| + it "should have a %s parameter" % parameter do + zpool.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) + end + end +end + +vdev_property = Puppet::Property::VDev + +describe vdev_property do + before do + vdev_property.initvars + @resource = stub 'resource', :[]= => nil, :property => nil + @property = vdev_property.new(:resource => @resource) + end + + it "should be insync if the devices are the same" do + @property.should = ["dev1 dev2"] + @property.insync?(["dev2 dev1"]).must be_true + end + + it "should be out of sync if the devices are not the same" do + @property.should = ["dev1 dev3"] + @property.insync?(["dev2 dev1"]).must be_false + end + + it "should be insync if the devices are the same and the should values are comma seperated" do + @property.should = ["dev1", "dev2"] + @property.insync?(["dev2 dev1"]).must be_true + end + + it "should be out of sync if the device is absent and should has a value" do + @property.should = ["dev1", "dev2"] + @property.insync?(:absent).must be_false + end + + it "should be insync if the device is absent and should is absent" do + @property.should = [:absent] + @property.insync?(:absent).must be_true + end +end + +multi_vdev_property = Puppet::Property::MultiVDev + +describe multi_vdev_property do + before do + multi_vdev_property.initvars + @resource = stub 'resource', :[]= => nil, :property => nil + @property = multi_vdev_property.new(:resource => @resource) + end + + it "should be insync if the devices are the same" do + @property.should = ["dev1 dev2"] + @property.insync?(["dev2 dev1"]).must be_true + end + + it "should be out of sync if the devices are not the same" do + @property.should = ["dev1 dev3"] + @property.insync?(["dev2 dev1"]).must be_false + end + + it "should be out of sync if the device is absent and should has a value" do + @property.should = ["dev1", "dev2"] + @property.insync?(:absent).must be_false + end + + it "should be insync if the device is absent and should is absent" do + @property.should = [:absent] + @property.insync?(:absent).must be_true + end + + describe "when there are multiple lists of devices" do + it "should be in sync if each group has the same devices" do + @property.should = ["dev1 dev2", "dev3 dev4"] + @property.insync?(["dev2 dev1", "dev3 dev4"]).must be_true + end + + it "should be out of sync if any group has the different devices" do + @property.should = ["dev1 devX", "dev3 dev4"] + @property.insync?(["dev2 dev1", "dev3 dev4"]).must be_false + end + + it "should be out of sync if devices are in the wrong group" do + @property.should = ["dev1 dev2", "dev3 dev4"] + @property.insync?(["dev2 dev3", "dev1 dev4"]).must be_false + end + end +end diff --git a/spec/unit/type/zpool_spec_spec.rb b/spec/unit/type/zpool_spec_spec.rb deleted file mode 100755 index c957e5684..000000000 --- a/spec/unit/type/zpool_spec_spec.rb +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -zpool = Puppet::Type.type(:zpool) - -describe zpool do - before do - @provider = stub 'provider' - @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil - end - - properties = [:ensure, :disk, :mirror, :raidz, :spare, :log] - - properties.each do |property| - it "should have a %s property" % property do - zpool.attrclass(property).ancestors.should be_include(Puppet::Property) - end - end - - parameters = [:pool, :raid_parity] - - parameters.each do |parameter| - it "should have a %s parameter" % parameter do - zpool.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) - end - end -end - -vdev_property = Puppet::Property::VDev - -describe vdev_property do - before do - vdev_property.initvars - @resource = stub 'resource', :[]= => nil, :property => nil - @property = vdev_property.new(:resource => @resource) - end - - it "should be insync if the devices are the same" do - @property.should = ["dev1 dev2"] - @property.insync?(["dev2 dev1"]).must be_true - end - - it "should be out of sync if the devices are not the same" do - @property.should = ["dev1 dev3"] - @property.insync?(["dev2 dev1"]).must be_false - end - - it "should be insync if the devices are the same and the should values are comma seperated" do - @property.should = ["dev1", "dev2"] - @property.insync?(["dev2 dev1"]).must be_true - end - - it "should be out of sync if the device is absent and should has a value" do - @property.should = ["dev1", "dev2"] - @property.insync?(:absent).must be_false - end - - it "should be insync if the device is absent and should is absent" do - @property.should = [:absent] - @property.insync?(:absent).must be_true - end -end - -multi_vdev_property = Puppet::Property::MultiVDev - -describe multi_vdev_property do - before do - multi_vdev_property.initvars - @resource = stub 'resource', :[]= => nil, :property => nil - @property = multi_vdev_property.new(:resource => @resource) - end - - it "should be insync if the devices are the same" do - @property.should = ["dev1 dev2"] - @property.insync?(["dev2 dev1"]).must be_true - end - - it "should be out of sync if the devices are not the same" do - @property.should = ["dev1 dev3"] - @property.insync?(["dev2 dev1"]).must be_false - end - - it "should be out of sync if the device is absent and should has a value" do - @property.should = ["dev1", "dev2"] - @property.insync?(:absent).must be_false - end - - it "should be insync if the device is absent and should is absent" do - @property.should = [:absent] - @property.insync?(:absent).must be_true - end - - describe "when there are multiple lists of devices" do - it "should be in sync if each group has the same devices" do - @property.should = ["dev1 dev2", "dev3 dev4"] - @property.insync?(["dev2 dev1", "dev3 dev4"]).must be_true - end - - it "should be out of sync if any group has the different devices" do - @property.should = ["dev1 devX", "dev3 dev4"] - @property.insync?(["dev2 dev1", "dev3 dev4"]).must be_false - end - - it "should be out of sync if devices are in the wrong group" do - @property.should = ["dev1 dev2", "dev3 dev4"] - @property.insync?(["dev2 dev3", "dev1 dev4"]).must be_false - end - end -end diff --git a/spec/unit/type_spec.rb b/spec/unit/type_spec.rb new file mode 100755 index 000000000..54fb2978c --- /dev/null +++ b/spec/unit/type_spec.rb @@ -0,0 +1,529 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +describe Puppet::Type do + it "should include the Cacher module" do + Puppet::Type.ancestors.should be_include(Puppet::Util::Cacher) + end + + it "should consider a parameter to be valid if it is a valid parameter" do + Puppet::Type.type(:mount).should be_valid_parameter(:path) + end + + it "should consider a parameter to be valid if it is a valid property" do + Puppet::Type.type(:mount).should be_valid_parameter(:fstype) + end + + it "should consider a parameter to be valid if it is a valid metaparam" do + Puppet::Type.type(:mount).should be_valid_parameter(:noop) + end + + it "should use its catalog as its expirer" do + catalog = Puppet::Resource::Catalog.new + resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) + resource.catalog = catalog + resource.expirer.should equal(catalog) + end + + it "should do nothing when asked to expire when it has no catalog" do + resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) + lambda { resource.expire }.should_not raise_error + end + + it "should be able to retrieve a property by name" do + resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) + resource.property(:fstype).must be_instance_of(Puppet::Type.type(:mount).attrclass(:fstype)) + end + + it "should be able to retrieve a parameter by name" do + resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) + resource.parameter(:name).must be_instance_of(Puppet::Type.type(:mount).attrclass(:name)) + end + + it "should be able to retrieve a property by name using the :parameter method" do + resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) + resource.parameter(:fstype).must be_instance_of(Puppet::Type.type(:mount).attrclass(:fstype)) + end + + it "should be able to retrieve all set properties" do + resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) + props = resource.properties + props.should_not be_include(nil) + [:fstype, :ensure, :pass].each do |name| + props.should be_include(resource.parameter(name)) + end + end + + it "should have a method for setting default values for resources" do + Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:set_default) + end + + it "should do nothing for attributes that have no defaults and no specified value" do + Puppet::Type.type(:mount).new(:name => "foo").parameter(:noop).should be_nil + end + + it "should have a method for adding tags" do + Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:tags) + end + + it "should use the tagging module" do + Puppet::Type.type(:mount).ancestors.should be_include(Puppet::Util::Tagging) + end + + it "should delegate to the tagging module when tags are added" do + resource = Puppet::Type.type(:mount).new(:name => "foo") + resource.stubs(:tag).with(:mount) + + resource.expects(:tag).with(:tag1, :tag2) + + resource.tags = [:tag1,:tag2] + end + + it "should add the current type as tag" do + resource = Puppet::Type.type(:mount).new(:name => "foo") + resource.stubs(:tag) + + resource.expects(:tag).with(:mount) + + resource.tags = [:tag1,:tag2] + end + + it "should have a method to know if the resource is exported" do + Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:exported?) + end + + it "should have a method to know if the resource is virtual" do + Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:virtual?) + end + + it "should consider its version to be its catalog version" do + resource = Puppet::Type.type(:mount).new(:name => "foo") + catalog = Puppet::Resource::Catalog.new + catalog.version = 50 + catalog.add_resource resource + + resource.version.should == 50 + end + + it "should consider its version to be zero if it has no catalog" do + Puppet::Type.type(:mount).new(:name => "foo").version.should == 0 + end + + it "should provide source_descriptors" do + resource = Puppet::Type.type(:mount).new(:name => "foo") + catalog = Puppet::Resource::Catalog.new + catalog.version = 50 + catalog.add_resource resource + + resource.source_descriptors.should == {:version=>50, :tags=>["mount", "foo"], :path=>"/Mount[foo]"} + end + + it "should consider its type to be the name of its class" do + Puppet::Type.type(:mount).new(:name => "foo").type.should == :mount + end + + describe "when creating an event" do + before do + @resource = Puppet::Type.type(:mount).new :name => "foo" + end + + it "should have the resource's reference as the resource" do + @resource.event.resource.should == "Mount[foo]" + end + + it "should have the resource's log level as the default log level" do + @resource[:loglevel] = :warning + @resource.event.default_log_level.should == :warning + end + + {:file => "/my/file", :line => 50, :tags => %{foo bar}, :version => 50}.each do |attr, value| + it "should set the #{attr}" do + @resource.stubs(attr).returns value + @resource.event.send(attr).should == value + end + end + + it "should allow specification of event attributes" do + @resource.event(:status => "noop").status.should == "noop" + end + end + + describe "when choosing a default provider" do + it "should choose the provider with the highest specificity" do + # Make a fake type + type = Puppet::Type.newtype(:defaultprovidertest) do + newparam(:name) do end + end + + basic = type.provide(:basic) {} + greater = type.provide(:greater) {} + + basic.stubs(:specificity).returns 1 + greater.stubs(:specificity).returns 2 + + type.defaultprovider.should equal(greater) + end + end + + describe "when initializing" do + describe "and passed a TransObject" do + it "should fail" do + trans = Puppet::TransObject.new("/foo", :mount) + lambda { Puppet::Type.type(:mount).new(trans) }.should raise_error(Puppet::DevError) + end + end + + describe "and passed a Puppet::Resource instance" do + it "should set its title to the title of the resource if the resource type is equal to the current type" do + resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "/other"}) + Puppet::Type.type(:mount).new(resource).title.should == "/foo" + end + + it "should set its title to the resource reference if the resource type is not equal to the current type" do + resource = Puppet::Resource.new(:user, "foo") + Puppet::Type.type(:mount).new(resource).title.should == "User[foo]" + end + + [:line, :file, :catalog, :exported, :virtual].each do |param| + it "should copy '#{param}' from the resource if present" do + resource = Puppet::Resource.new(:mount, "/foo") + resource.send(param.to_s + "=", "foo") + resource.send(param.to_s + "=", "foo") + Puppet::Type.type(:mount).new(resource).send(param).should == "foo" + end + end + + it "should copy any tags from the resource" do + resource = Puppet::Resource.new(:mount, "/foo") + resource.tag "one", "two" + tags = Puppet::Type.type(:mount).new(resource).tags + tags.should be_include("one") + tags.should be_include("two") + end + + it "should copy the resource's parameters as its own" do + resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:atboot => true, :fstype => "boo"}) + params = Puppet::Type.type(:mount).new(resource).to_hash + params[:fstype].should == "boo" + params[:atboot].should == true + end + end + + describe "and passed a Hash" do + it "should extract the title from the hash" do + Puppet::Type.type(:mount).new(:title => "/yay").title.should == "/yay" + end + + it "should work when hash keys are provided as strings" do + Puppet::Type.type(:mount).new("title" => "/yay").title.should == "/yay" + end + + it "should work when hash keys are provided as symbols" do + Puppet::Type.type(:mount).new(:title => "/yay").title.should == "/yay" + end + + it "should use the name from the hash as the title if no explicit title is provided" do + Puppet::Type.type(:mount).new(:name => "/yay").title.should == "/yay" + end + + it "should use the Resource Type's namevar to determine how to find the name in the hash" do + Puppet::Type.type(:file).new(:path => "/yay").title.should == "/yay" + end + + [:catalog].each do |param| + it "should extract '#{param}' from the hash if present" do + Puppet::Type.type(:mount).new(:name => "/yay", param => "foo").send(param).should == "foo" + end + end + + it "should use any remaining hash keys as its parameters" do + resource = Puppet::Type.type(:mount).new(:title => "/foo", :catalog => "foo", :atboot => true, :fstype => "boo") + resource[:fstype].must == "boo" + resource[:atboot].must == true + end + end + + it "should fail if any invalid attributes have been provided" do + lambda { Puppet::Type.type(:mount).new(:title => "/foo", :nosuchattr => "whatever") }.should raise_error(Puppet::Error) + end + + it "should set its name to the resource's title if the resource does not have a :name or namevar parameter set" do + resource = Puppet::Resource.new(:mount, "/foo") + + Puppet::Type.type(:mount).new(resource).name.should == "/foo" + end + + it "should fail if no title, name, or namevar are provided" do + lambda { Puppet::Type.type(:file).new(:atboot => true) }.should raise_error(Puppet::Error) + end + + it "should set the attributes in the order returned by the class's :allattrs method" do + Puppet::Type.type(:mount).stubs(:allattrs).returns([:name, :atboot, :noop]) + resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "myname", :atboot => "myboot", :noop => "whatever"}) + + set = [] + + Puppet::Type.type(:mount).any_instance.stubs(:newattr).with do |param, hash| + set << param + true + end.returns(stub_everything("a property")) + + Puppet::Type.type(:mount).new(resource) + + set[-1].should == :noop + set[-2].should == :atboot + end + + it "should always set the name and then default provider before anything else" do + Puppet::Type.type(:mount).stubs(:allattrs).returns([:provider, :name, :atboot]) + resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "myname", :atboot => "myboot"}) + + set = [] + + Puppet::Type.type(:mount).any_instance.stubs(:newattr).with do |param, hash| + set << param + true + end.returns(stub_everything("a property")) + + Puppet::Type.type(:mount).new(resource) + set[0].should == :name + set[1].should == :provider + end + + # This one is really hard to test :/ + it "should each default immediately if no value is provided" do + defaults = [] + Puppet::Type.type(:package).any_instance.stubs(:set_default).with { |value| defaults << value; true } + + Puppet::Type.type(:package).new :name => "whatever" + + defaults[0].should == :provider + end + + it "should retain a copy of the originally provided parameters" do + Puppet::Type.type(:mount).new(:name => "foo", :atboot => true, :noop => false).original_parameters.should == {:atboot => true, :noop => false} + end + + it "should delete the name via the namevar from the originally provided parameters" do + Puppet::Type.type(:file).new(:name => "/foo").original_parameters[:path].should be_nil + end + end + + it "should have a class method for converting a hash into a Puppet::Resource instance" do + Puppet::Type.type(:mount).must respond_to(:hash2resource) + end + + describe "when converting a hash to a Puppet::Resource instance" do + before do + @type = Puppet::Type.type(:mount) + end + + it "should treat a :title key as the title of the resource" do + @type.hash2resource(:name => "/foo", :title => "foo").title.should == "foo" + end + + it "should use the name from the hash as the title if no explicit title is provided" do + @type.hash2resource(:name => "foo").title.should == "foo" + end + + it "should use the Resource Type's namevar to determine how to find the name in the hash" do + @type.stubs(:key_attributes).returns([ :myname ]) + + @type.hash2resource(:myname => "foo").title.should == "foo" + end + + [:catalog].each do |attr| + it "should use any provided #{attr}" do + @type.hash2resource(:name => "foo", attr => "eh").send(attr).should == "eh" + end + end + + it "should set all provided parameters on the resource" do + @type.hash2resource(:name => "foo", :fstype => "boo", :boot => "fee").to_hash.should == {:name => "foo", :fstype => "boo", :boot => "fee"} + end + + it "should not set the title as a parameter on the resource" do + @type.hash2resource(:name => "foo", :title => "eh")[:title].should be_nil + end + + it "should not set the catalog as a parameter on the resource" do + @type.hash2resource(:name => "foo", :catalog => "eh")[:catalog].should be_nil + end + + it "should treat hash keys equivalently whether provided as strings or symbols" do + resource = @type.hash2resource("name" => "foo", "title" => "eh", "fstype" => "boo") + resource.title.should == "eh" + resource[:name].should == "foo" + resource[:fstype].should == "boo" + end + end + + describe "when retrieving current property values" do + before do + @resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) + @resource.property(:ensure).stubs(:retrieve).returns :absent + end + + it "should fail if its provider is unsuitable" do + @resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) + @resource.provider.class.expects(:suitable?).returns false + lambda { @resource.retrieve }.should raise_error(Puppet::Error) + end + + it "should return a Puppet::Resource instance with its type and title set appropriately" do + result = @resource.retrieve + result.should be_instance_of(Puppet::Resource) + result.type.should == "Mount" + result.title.should == "foo" + end + + it "should set the name of the returned resource if its own name and title differ" do + @resource[:name] = "my name" + @resource.title = "other name" + @resource.retrieve[:name].should == "my name" + end + + it "should provide a value for all set properties" do + values = @resource.retrieve + [:ensure, :fstype, :pass].each { |property| values[property].should_not be_nil } + end + + it "should provide a value for 'ensure' even if no desired value is provided" do + @resource = Puppet::Type.type(:file).new(:path => "/my/file/that/can't/exist") + end + + it "should not call retrieve on non-ensure properties if the resource is absent and should consider the property absent" do + @resource.property(:ensure).expects(:retrieve).returns :absent + @resource.property(:fstype).expects(:retrieve).never + @resource.retrieve[:fstype].should == :absent + end + + it "should include the result of retrieving each property's current value if the resource is present" do + @resource.property(:ensure).expects(:retrieve).returns :present + @resource.property(:fstype).expects(:retrieve).returns 15 + @resource.retrieve[:fstype] == 15 + end + end + + + describe "when in a catalog" do + before do + @catalog = Puppet::Resource::Catalog.new + @container = Puppet::Type.type(:component).new(:name => "container") + @one = Puppet::Type.type(:file).new(:path => "/file/one") + @two = Puppet::Type.type(:file).new(:path => "/file/two") + + @catalog.add_resource @container + @catalog.add_resource @one + @catalog.add_resource @two + @catalog.add_edge @container, @one + @catalog.add_edge @container, @two + end + + it "should have no parent if there is no in edge" do + @container.parent.should be_nil + end + + it "should set its parent to its in edge" do + @one.parent.ref.should == @container.ref + end + + after do + @catalog.clear(true) + end + end + + it "should have a 'stage' metaparam" do + Puppet::Type.metaparamclass(:stage).should be_instance_of(Class) + end +end + +describe Puppet::Type::RelationshipMetaparam do + it "should be a subclass of Puppet::Parameter" do + Puppet::Type::RelationshipMetaparam.superclass.should equal(Puppet::Parameter) + end + + it "should be able to produce a list of subclasses" do + Puppet::Type::RelationshipMetaparam.should respond_to(:subclasses) + end + + describe "when munging relationships" do + before do + @resource = Puppet::Type.type(:mount).new :name => "/foo" + @metaparam = Puppet::Type.metaparamclass(:require).new :resource => @resource + end + + it "should accept Puppet::Resource instances" do + ref = Puppet::Resource.new(:file, "/foo") + @metaparam.munge(ref)[0].should equal(ref) + end + + it "should turn any string into a Puppet::Resource" do + @metaparam.munge("File[/ref]")[0].should be_instance_of(Puppet::Resource) + end + end + + it "should be able to validate relationships" do + Puppet::Type.metaparamclass(:require).new(:resource => mock("resource")).should respond_to(:validate_relationship) + end + + it "should fail if any specified resource is not found in the catalog" do + catalog = mock 'catalog' + resource = stub 'resource', :catalog => catalog, :ref => "resource" + + param = Puppet::Type.metaparamclass(:require).new(:resource => resource, :value => %w{Foo[bar] Class[test]}) + + catalog.expects(:resource).with("Foo[bar]").returns "something" + catalog.expects(:resource).with("Class[Test]").returns nil + + param.expects(:fail).with { |string| string.include?("Class[Test]") } + + param.validate_relationship + end +end + +describe Puppet::Type.metaparamclass(:check) do + it "should warn and create an instance of ':audit'" do + file = Puppet::Type.type(:file).new :path => "/foo" + file.expects(:warning) + file[:check] = :mode + file[:audit].should == [:mode] + end +end + +describe Puppet::Type.metaparamclass(:audit) do + before do + @resource = Puppet::Type.type(:file).new :path => "/foo" + end + + it "should default to being nil" do + @resource[:audit].should be_nil + end + + it "should specify all possible properties when asked to audit all properties" do + @resource[:audit] = :all + + list = @resource.class.properties.collect { |p| p.name } + @resource[:audit].should == list + end + + it "should fail if asked to audit an invalid property" do + lambda { @resource[:audit] = :foobar }.should raise_error(Puppet::Error) + end + + it "should create an attribute instance for each auditable property" do + @resource[:audit] = :mode + @resource.parameter(:mode).should_not be_nil + end + + it "should accept properties specified as a string" do + @resource[:audit] = "mode" + @resource.parameter(:mode).should_not be_nil + end + + it "should not create attribute instances for parameters, only properties" do + @resource[:audit] = :noop + @resource.parameter(:noop).should be_nil + end +end diff --git a/spec/unit/type_spec_spec.rb b/spec/unit/type_spec_spec.rb deleted file mode 100755 index 54fb2978c..000000000 --- a/spec/unit/type_spec_spec.rb +++ /dev/null @@ -1,529 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../spec_helper' - -describe Puppet::Type do - it "should include the Cacher module" do - Puppet::Type.ancestors.should be_include(Puppet::Util::Cacher) - end - - it "should consider a parameter to be valid if it is a valid parameter" do - Puppet::Type.type(:mount).should be_valid_parameter(:path) - end - - it "should consider a parameter to be valid if it is a valid property" do - Puppet::Type.type(:mount).should be_valid_parameter(:fstype) - end - - it "should consider a parameter to be valid if it is a valid metaparam" do - Puppet::Type.type(:mount).should be_valid_parameter(:noop) - end - - it "should use its catalog as its expirer" do - catalog = Puppet::Resource::Catalog.new - resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) - resource.catalog = catalog - resource.expirer.should equal(catalog) - end - - it "should do nothing when asked to expire when it has no catalog" do - resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) - lambda { resource.expire }.should_not raise_error - end - - it "should be able to retrieve a property by name" do - resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) - resource.property(:fstype).must be_instance_of(Puppet::Type.type(:mount).attrclass(:fstype)) - end - - it "should be able to retrieve a parameter by name" do - resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) - resource.parameter(:name).must be_instance_of(Puppet::Type.type(:mount).attrclass(:name)) - end - - it "should be able to retrieve a property by name using the :parameter method" do - resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) - resource.parameter(:fstype).must be_instance_of(Puppet::Type.type(:mount).attrclass(:fstype)) - end - - it "should be able to retrieve all set properties" do - resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) - props = resource.properties - props.should_not be_include(nil) - [:fstype, :ensure, :pass].each do |name| - props.should be_include(resource.parameter(name)) - end - end - - it "should have a method for setting default values for resources" do - Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:set_default) - end - - it "should do nothing for attributes that have no defaults and no specified value" do - Puppet::Type.type(:mount).new(:name => "foo").parameter(:noop).should be_nil - end - - it "should have a method for adding tags" do - Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:tags) - end - - it "should use the tagging module" do - Puppet::Type.type(:mount).ancestors.should be_include(Puppet::Util::Tagging) - end - - it "should delegate to the tagging module when tags are added" do - resource = Puppet::Type.type(:mount).new(:name => "foo") - resource.stubs(:tag).with(:mount) - - resource.expects(:tag).with(:tag1, :tag2) - - resource.tags = [:tag1,:tag2] - end - - it "should add the current type as tag" do - resource = Puppet::Type.type(:mount).new(:name => "foo") - resource.stubs(:tag) - - resource.expects(:tag).with(:mount) - - resource.tags = [:tag1,:tag2] - end - - it "should have a method to know if the resource is exported" do - Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:exported?) - end - - it "should have a method to know if the resource is virtual" do - Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:virtual?) - end - - it "should consider its version to be its catalog version" do - resource = Puppet::Type.type(:mount).new(:name => "foo") - catalog = Puppet::Resource::Catalog.new - catalog.version = 50 - catalog.add_resource resource - - resource.version.should == 50 - end - - it "should consider its version to be zero if it has no catalog" do - Puppet::Type.type(:mount).new(:name => "foo").version.should == 0 - end - - it "should provide source_descriptors" do - resource = Puppet::Type.type(:mount).new(:name => "foo") - catalog = Puppet::Resource::Catalog.new - catalog.version = 50 - catalog.add_resource resource - - resource.source_descriptors.should == {:version=>50, :tags=>["mount", "foo"], :path=>"/Mount[foo]"} - end - - it "should consider its type to be the name of its class" do - Puppet::Type.type(:mount).new(:name => "foo").type.should == :mount - end - - describe "when creating an event" do - before do - @resource = Puppet::Type.type(:mount).new :name => "foo" - end - - it "should have the resource's reference as the resource" do - @resource.event.resource.should == "Mount[foo]" - end - - it "should have the resource's log level as the default log level" do - @resource[:loglevel] = :warning - @resource.event.default_log_level.should == :warning - end - - {:file => "/my/file", :line => 50, :tags => %{foo bar}, :version => 50}.each do |attr, value| - it "should set the #{attr}" do - @resource.stubs(attr).returns value - @resource.event.send(attr).should == value - end - end - - it "should allow specification of event attributes" do - @resource.event(:status => "noop").status.should == "noop" - end - end - - describe "when choosing a default provider" do - it "should choose the provider with the highest specificity" do - # Make a fake type - type = Puppet::Type.newtype(:defaultprovidertest) do - newparam(:name) do end - end - - basic = type.provide(:basic) {} - greater = type.provide(:greater) {} - - basic.stubs(:specificity).returns 1 - greater.stubs(:specificity).returns 2 - - type.defaultprovider.should equal(greater) - end - end - - describe "when initializing" do - describe "and passed a TransObject" do - it "should fail" do - trans = Puppet::TransObject.new("/foo", :mount) - lambda { Puppet::Type.type(:mount).new(trans) }.should raise_error(Puppet::DevError) - end - end - - describe "and passed a Puppet::Resource instance" do - it "should set its title to the title of the resource if the resource type is equal to the current type" do - resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "/other"}) - Puppet::Type.type(:mount).new(resource).title.should == "/foo" - end - - it "should set its title to the resource reference if the resource type is not equal to the current type" do - resource = Puppet::Resource.new(:user, "foo") - Puppet::Type.type(:mount).new(resource).title.should == "User[foo]" - end - - [:line, :file, :catalog, :exported, :virtual].each do |param| - it "should copy '#{param}' from the resource if present" do - resource = Puppet::Resource.new(:mount, "/foo") - resource.send(param.to_s + "=", "foo") - resource.send(param.to_s + "=", "foo") - Puppet::Type.type(:mount).new(resource).send(param).should == "foo" - end - end - - it "should copy any tags from the resource" do - resource = Puppet::Resource.new(:mount, "/foo") - resource.tag "one", "two" - tags = Puppet::Type.type(:mount).new(resource).tags - tags.should be_include("one") - tags.should be_include("two") - end - - it "should copy the resource's parameters as its own" do - resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:atboot => true, :fstype => "boo"}) - params = Puppet::Type.type(:mount).new(resource).to_hash - params[:fstype].should == "boo" - params[:atboot].should == true - end - end - - describe "and passed a Hash" do - it "should extract the title from the hash" do - Puppet::Type.type(:mount).new(:title => "/yay").title.should == "/yay" - end - - it "should work when hash keys are provided as strings" do - Puppet::Type.type(:mount).new("title" => "/yay").title.should == "/yay" - end - - it "should work when hash keys are provided as symbols" do - Puppet::Type.type(:mount).new(:title => "/yay").title.should == "/yay" - end - - it "should use the name from the hash as the title if no explicit title is provided" do - Puppet::Type.type(:mount).new(:name => "/yay").title.should == "/yay" - end - - it "should use the Resource Type's namevar to determine how to find the name in the hash" do - Puppet::Type.type(:file).new(:path => "/yay").title.should == "/yay" - end - - [:catalog].each do |param| - it "should extract '#{param}' from the hash if present" do - Puppet::Type.type(:mount).new(:name => "/yay", param => "foo").send(param).should == "foo" - end - end - - it "should use any remaining hash keys as its parameters" do - resource = Puppet::Type.type(:mount).new(:title => "/foo", :catalog => "foo", :atboot => true, :fstype => "boo") - resource[:fstype].must == "boo" - resource[:atboot].must == true - end - end - - it "should fail if any invalid attributes have been provided" do - lambda { Puppet::Type.type(:mount).new(:title => "/foo", :nosuchattr => "whatever") }.should raise_error(Puppet::Error) - end - - it "should set its name to the resource's title if the resource does not have a :name or namevar parameter set" do - resource = Puppet::Resource.new(:mount, "/foo") - - Puppet::Type.type(:mount).new(resource).name.should == "/foo" - end - - it "should fail if no title, name, or namevar are provided" do - lambda { Puppet::Type.type(:file).new(:atboot => true) }.should raise_error(Puppet::Error) - end - - it "should set the attributes in the order returned by the class's :allattrs method" do - Puppet::Type.type(:mount).stubs(:allattrs).returns([:name, :atboot, :noop]) - resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "myname", :atboot => "myboot", :noop => "whatever"}) - - set = [] - - Puppet::Type.type(:mount).any_instance.stubs(:newattr).with do |param, hash| - set << param - true - end.returns(stub_everything("a property")) - - Puppet::Type.type(:mount).new(resource) - - set[-1].should == :noop - set[-2].should == :atboot - end - - it "should always set the name and then default provider before anything else" do - Puppet::Type.type(:mount).stubs(:allattrs).returns([:provider, :name, :atboot]) - resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "myname", :atboot => "myboot"}) - - set = [] - - Puppet::Type.type(:mount).any_instance.stubs(:newattr).with do |param, hash| - set << param - true - end.returns(stub_everything("a property")) - - Puppet::Type.type(:mount).new(resource) - set[0].should == :name - set[1].should == :provider - end - - # This one is really hard to test :/ - it "should each default immediately if no value is provided" do - defaults = [] - Puppet::Type.type(:package).any_instance.stubs(:set_default).with { |value| defaults << value; true } - - Puppet::Type.type(:package).new :name => "whatever" - - defaults[0].should == :provider - end - - it "should retain a copy of the originally provided parameters" do - Puppet::Type.type(:mount).new(:name => "foo", :atboot => true, :noop => false).original_parameters.should == {:atboot => true, :noop => false} - end - - it "should delete the name via the namevar from the originally provided parameters" do - Puppet::Type.type(:file).new(:name => "/foo").original_parameters[:path].should be_nil - end - end - - it "should have a class method for converting a hash into a Puppet::Resource instance" do - Puppet::Type.type(:mount).must respond_to(:hash2resource) - end - - describe "when converting a hash to a Puppet::Resource instance" do - before do - @type = Puppet::Type.type(:mount) - end - - it "should treat a :title key as the title of the resource" do - @type.hash2resource(:name => "/foo", :title => "foo").title.should == "foo" - end - - it "should use the name from the hash as the title if no explicit title is provided" do - @type.hash2resource(:name => "foo").title.should == "foo" - end - - it "should use the Resource Type's namevar to determine how to find the name in the hash" do - @type.stubs(:key_attributes).returns([ :myname ]) - - @type.hash2resource(:myname => "foo").title.should == "foo" - end - - [:catalog].each do |attr| - it "should use any provided #{attr}" do - @type.hash2resource(:name => "foo", attr => "eh").send(attr).should == "eh" - end - end - - it "should set all provided parameters on the resource" do - @type.hash2resource(:name => "foo", :fstype => "boo", :boot => "fee").to_hash.should == {:name => "foo", :fstype => "boo", :boot => "fee"} - end - - it "should not set the title as a parameter on the resource" do - @type.hash2resource(:name => "foo", :title => "eh")[:title].should be_nil - end - - it "should not set the catalog as a parameter on the resource" do - @type.hash2resource(:name => "foo", :catalog => "eh")[:catalog].should be_nil - end - - it "should treat hash keys equivalently whether provided as strings or symbols" do - resource = @type.hash2resource("name" => "foo", "title" => "eh", "fstype" => "boo") - resource.title.should == "eh" - resource[:name].should == "foo" - resource[:fstype].should == "boo" - end - end - - describe "when retrieving current property values" do - before do - @resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) - @resource.property(:ensure).stubs(:retrieve).returns :absent - end - - it "should fail if its provider is unsuitable" do - @resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) - @resource.provider.class.expects(:suitable?).returns false - lambda { @resource.retrieve }.should raise_error(Puppet::Error) - end - - it "should return a Puppet::Resource instance with its type and title set appropriately" do - result = @resource.retrieve - result.should be_instance_of(Puppet::Resource) - result.type.should == "Mount" - result.title.should == "foo" - end - - it "should set the name of the returned resource if its own name and title differ" do - @resource[:name] = "my name" - @resource.title = "other name" - @resource.retrieve[:name].should == "my name" - end - - it "should provide a value for all set properties" do - values = @resource.retrieve - [:ensure, :fstype, :pass].each { |property| values[property].should_not be_nil } - end - - it "should provide a value for 'ensure' even if no desired value is provided" do - @resource = Puppet::Type.type(:file).new(:path => "/my/file/that/can't/exist") - end - - it "should not call retrieve on non-ensure properties if the resource is absent and should consider the property absent" do - @resource.property(:ensure).expects(:retrieve).returns :absent - @resource.property(:fstype).expects(:retrieve).never - @resource.retrieve[:fstype].should == :absent - end - - it "should include the result of retrieving each property's current value if the resource is present" do - @resource.property(:ensure).expects(:retrieve).returns :present - @resource.property(:fstype).expects(:retrieve).returns 15 - @resource.retrieve[:fstype] == 15 - end - end - - - describe "when in a catalog" do - before do - @catalog = Puppet::Resource::Catalog.new - @container = Puppet::Type.type(:component).new(:name => "container") - @one = Puppet::Type.type(:file).new(:path => "/file/one") - @two = Puppet::Type.type(:file).new(:path => "/file/two") - - @catalog.add_resource @container - @catalog.add_resource @one - @catalog.add_resource @two - @catalog.add_edge @container, @one - @catalog.add_edge @container, @two - end - - it "should have no parent if there is no in edge" do - @container.parent.should be_nil - end - - it "should set its parent to its in edge" do - @one.parent.ref.should == @container.ref - end - - after do - @catalog.clear(true) - end - end - - it "should have a 'stage' metaparam" do - Puppet::Type.metaparamclass(:stage).should be_instance_of(Class) - end -end - -describe Puppet::Type::RelationshipMetaparam do - it "should be a subclass of Puppet::Parameter" do - Puppet::Type::RelationshipMetaparam.superclass.should equal(Puppet::Parameter) - end - - it "should be able to produce a list of subclasses" do - Puppet::Type::RelationshipMetaparam.should respond_to(:subclasses) - end - - describe "when munging relationships" do - before do - @resource = Puppet::Type.type(:mount).new :name => "/foo" - @metaparam = Puppet::Type.metaparamclass(:require).new :resource => @resource - end - - it "should accept Puppet::Resource instances" do - ref = Puppet::Resource.new(:file, "/foo") - @metaparam.munge(ref)[0].should equal(ref) - end - - it "should turn any string into a Puppet::Resource" do - @metaparam.munge("File[/ref]")[0].should be_instance_of(Puppet::Resource) - end - end - - it "should be able to validate relationships" do - Puppet::Type.metaparamclass(:require).new(:resource => mock("resource")).should respond_to(:validate_relationship) - end - - it "should fail if any specified resource is not found in the catalog" do - catalog = mock 'catalog' - resource = stub 'resource', :catalog => catalog, :ref => "resource" - - param = Puppet::Type.metaparamclass(:require).new(:resource => resource, :value => %w{Foo[bar] Class[test]}) - - catalog.expects(:resource).with("Foo[bar]").returns "something" - catalog.expects(:resource).with("Class[Test]").returns nil - - param.expects(:fail).with { |string| string.include?("Class[Test]") } - - param.validate_relationship - end -end - -describe Puppet::Type.metaparamclass(:check) do - it "should warn and create an instance of ':audit'" do - file = Puppet::Type.type(:file).new :path => "/foo" - file.expects(:warning) - file[:check] = :mode - file[:audit].should == [:mode] - end -end - -describe Puppet::Type.metaparamclass(:audit) do - before do - @resource = Puppet::Type.type(:file).new :path => "/foo" - end - - it "should default to being nil" do - @resource[:audit].should be_nil - end - - it "should specify all possible properties when asked to audit all properties" do - @resource[:audit] = :all - - list = @resource.class.properties.collect { |p| p.name } - @resource[:audit].should == list - end - - it "should fail if asked to audit an invalid property" do - lambda { @resource[:audit] = :foobar }.should raise_error(Puppet::Error) - end - - it "should create an attribute instance for each auditable property" do - @resource[:audit] = :mode - @resource.parameter(:mode).should_not be_nil - end - - it "should accept properties specified as a string" do - @resource[:audit] = "mode" - @resource.parameter(:mode).should_not be_nil - end - - it "should not create attribute instances for parameters, only properties" do - @resource[:audit] = :noop - @resource.parameter(:noop).should be_nil - end -end diff --git a/spec/unit/util/autoload/file_cache_spec.rb b/spec/unit/util/autoload/file_cache_spec.rb new file mode 100755 index 000000000..d52ea453d --- /dev/null +++ b/spec/unit/util/autoload/file_cache_spec.rb @@ -0,0 +1,159 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/util/autoload/file_cache' + +class FileCacheTester + include Puppet::Util::Autoload::FileCache +end + +describe Puppet::Util::Autoload::FileCache do + before do + @cacher = FileCacheTester.new + end + + after do + Puppet::Util::Autoload::FileCache.clear + end + + describe "when checking whether files exist" do + it "should have a method for testing whether a file exists" do + @cacher.should respond_to(:file_exist?) + end + + it "should use lstat to determine whether a file exists" do + File.expects(:lstat).with("/my/file") + @cacher.file_exist?("/my/file") + end + + it "should consider a file as absent if its lstat fails" do + File.expects(:lstat).with("/my/file").raises Errno::ENOENT + @cacher.should_not be_file_exist("/my/file") + end + + it "should consider a file as absent if the directory is absent" do + File.expects(:lstat).with("/my/file").raises Errno::ENOTDIR + @cacher.should_not be_file_exist("/my/file") + end + + it "should consider a file as absent permissions are missing" do + File.expects(:lstat).with("/my/file").raises Errno::EACCES + @cacher.should_not be_file_exist("/my/file") + end + + it "should raise non-fs exceptions" do + File.expects(:lstat).with("/my/file").raises ArgumentError + lambda { @cacher.file_exist?("/my/file") }.should raise_error(ArgumentError) + end + + it "should consider a file as present if its lstat succeeds" do + File.expects(:lstat).with("/my/file").returns mock("stat") + @cacher.should be_file_exist("/my/file") + end + + it "should not stat a file twice in quick succession when the file is missing" do + File.expects(:lstat).with("/my/file").once.raises Errno::ENOENT + @cacher.should_not be_file_exist("/my/file") + @cacher.should_not be_file_exist("/my/file") + end + + it "should not stat a file twice in quick succession when the file is present" do + File.expects(:lstat).with("/my/file").once.returns mock("stat") + @cacher.should be_file_exist("/my/file") + @cacher.should be_file_exist("/my/file") + end + + it "should expire cached data after 15 seconds" do + now = Time.now + + later = now + 16 + + Time.expects(:now).times(3).returns(now).then.returns(later).then.returns(later) + File.expects(:lstat).with("/my/file").times(2).returns(mock("stat")).then.raises Errno::ENOENT + @cacher.should be_file_exist("/my/file") + @cacher.should_not be_file_exist("/my/file") + end + + it "should share cached data across autoload instances" do + File.expects(:lstat).with("/my/file").once.returns mock("stat") + other = Puppet::Util::Autoload.new("bar", "tmp") + + @cacher.should be_file_exist("/my/file") + other.should be_file_exist("/my/file") + end + end + + describe "when checking whether files exist" do + before do + @stat = stub 'stat', :directory? => true + end + + it "should have a method for determining whether a directory exists" do + @cacher.should respond_to(:directory_exist?) + end + + it "should use lstat to determine whether a directory exists" do + File.expects(:lstat).with("/my/file").returns @stat + @cacher.directory_exist?("/my/file") + end + + it "should consider a directory as absent if its lstat fails" do + File.expects(:lstat).with("/my/file").raises Errno::ENOENT + @cacher.should_not be_directory_exist("/my/file") + end + + it "should consider a file as absent if the directory is absent" do + File.expects(:lstat).with("/my/file").raises Errno::ENOTDIR + @cacher.should_not be_directory_exist("/my/file") + end + + it "should consider a file as absent permissions are missing" do + File.expects(:lstat).with("/my/file").raises Errno::EACCES + @cacher.should_not be_directory_exist("/my/file") + end + + it "should raise non-fs exceptions" do + File.expects(:lstat).with("/my/file").raises ArgumentError + lambda { @cacher.directory_exist?("/my/file") }.should raise_error(ArgumentError) + end + + it "should consider a directory as present if its lstat succeeds and the stat is of a directory" do + @stat.expects(:directory?).returns true + File.expects(:lstat).with("/my/file").returns @stat + @cacher.should be_directory_exist("/my/file") + end + + it "should consider a directory as absent if its lstat succeeds and the stat is not of a directory" do + @stat.expects(:directory?).returns false + File.expects(:lstat).with("/my/file").returns @stat + @cacher.should_not be_directory_exist("/my/file") + end + + it "should not stat a directory twice in quick succession when the file is missing" do + File.expects(:lstat).with("/my/file").once.raises Errno::ENOENT + @cacher.should_not be_directory_exist("/my/file") + @cacher.should_not be_directory_exist("/my/file") + end + + it "should not stat a directory twice in quick succession when the file is present" do + File.expects(:lstat).with("/my/file").once.returns @stat + @cacher.should be_directory_exist("/my/file") + @cacher.should be_directory_exist("/my/file") + end + + it "should not consider a file to be a directory based on cached data" do + @stat.stubs(:directory?).returns false + File.stubs(:lstat).with("/my/file").returns @stat + @cacher.file_exist?("/my/file") + @cacher.should_not be_directory_exist("/my/file") + end + + it "should share cached data across autoload instances" do + File.expects(:lstat).with("/my/file").once.returns @stat + other = Puppet::Util::Autoload.new("bar", "tmp") + + @cacher.should be_directory_exist("/my/file") + other.should be_directory_exist("/my/file") + end + end +end diff --git a/spec/unit/util/autoload/file_cache_spec_spec.rb b/spec/unit/util/autoload/file_cache_spec_spec.rb deleted file mode 100755 index d52ea453d..000000000 --- a/spec/unit/util/autoload/file_cache_spec_spec.rb +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/util/autoload/file_cache' - -class FileCacheTester - include Puppet::Util::Autoload::FileCache -end - -describe Puppet::Util::Autoload::FileCache do - before do - @cacher = FileCacheTester.new - end - - after do - Puppet::Util::Autoload::FileCache.clear - end - - describe "when checking whether files exist" do - it "should have a method for testing whether a file exists" do - @cacher.should respond_to(:file_exist?) - end - - it "should use lstat to determine whether a file exists" do - File.expects(:lstat).with("/my/file") - @cacher.file_exist?("/my/file") - end - - it "should consider a file as absent if its lstat fails" do - File.expects(:lstat).with("/my/file").raises Errno::ENOENT - @cacher.should_not be_file_exist("/my/file") - end - - it "should consider a file as absent if the directory is absent" do - File.expects(:lstat).with("/my/file").raises Errno::ENOTDIR - @cacher.should_not be_file_exist("/my/file") - end - - it "should consider a file as absent permissions are missing" do - File.expects(:lstat).with("/my/file").raises Errno::EACCES - @cacher.should_not be_file_exist("/my/file") - end - - it "should raise non-fs exceptions" do - File.expects(:lstat).with("/my/file").raises ArgumentError - lambda { @cacher.file_exist?("/my/file") }.should raise_error(ArgumentError) - end - - it "should consider a file as present if its lstat succeeds" do - File.expects(:lstat).with("/my/file").returns mock("stat") - @cacher.should be_file_exist("/my/file") - end - - it "should not stat a file twice in quick succession when the file is missing" do - File.expects(:lstat).with("/my/file").once.raises Errno::ENOENT - @cacher.should_not be_file_exist("/my/file") - @cacher.should_not be_file_exist("/my/file") - end - - it "should not stat a file twice in quick succession when the file is present" do - File.expects(:lstat).with("/my/file").once.returns mock("stat") - @cacher.should be_file_exist("/my/file") - @cacher.should be_file_exist("/my/file") - end - - it "should expire cached data after 15 seconds" do - now = Time.now - - later = now + 16 - - Time.expects(:now).times(3).returns(now).then.returns(later).then.returns(later) - File.expects(:lstat).with("/my/file").times(2).returns(mock("stat")).then.raises Errno::ENOENT - @cacher.should be_file_exist("/my/file") - @cacher.should_not be_file_exist("/my/file") - end - - it "should share cached data across autoload instances" do - File.expects(:lstat).with("/my/file").once.returns mock("stat") - other = Puppet::Util::Autoload.new("bar", "tmp") - - @cacher.should be_file_exist("/my/file") - other.should be_file_exist("/my/file") - end - end - - describe "when checking whether files exist" do - before do - @stat = stub 'stat', :directory? => true - end - - it "should have a method for determining whether a directory exists" do - @cacher.should respond_to(:directory_exist?) - end - - it "should use lstat to determine whether a directory exists" do - File.expects(:lstat).with("/my/file").returns @stat - @cacher.directory_exist?("/my/file") - end - - it "should consider a directory as absent if its lstat fails" do - File.expects(:lstat).with("/my/file").raises Errno::ENOENT - @cacher.should_not be_directory_exist("/my/file") - end - - it "should consider a file as absent if the directory is absent" do - File.expects(:lstat).with("/my/file").raises Errno::ENOTDIR - @cacher.should_not be_directory_exist("/my/file") - end - - it "should consider a file as absent permissions are missing" do - File.expects(:lstat).with("/my/file").raises Errno::EACCES - @cacher.should_not be_directory_exist("/my/file") - end - - it "should raise non-fs exceptions" do - File.expects(:lstat).with("/my/file").raises ArgumentError - lambda { @cacher.directory_exist?("/my/file") }.should raise_error(ArgumentError) - end - - it "should consider a directory as present if its lstat succeeds and the stat is of a directory" do - @stat.expects(:directory?).returns true - File.expects(:lstat).with("/my/file").returns @stat - @cacher.should be_directory_exist("/my/file") - end - - it "should consider a directory as absent if its lstat succeeds and the stat is not of a directory" do - @stat.expects(:directory?).returns false - File.expects(:lstat).with("/my/file").returns @stat - @cacher.should_not be_directory_exist("/my/file") - end - - it "should not stat a directory twice in quick succession when the file is missing" do - File.expects(:lstat).with("/my/file").once.raises Errno::ENOENT - @cacher.should_not be_directory_exist("/my/file") - @cacher.should_not be_directory_exist("/my/file") - end - - it "should not stat a directory twice in quick succession when the file is present" do - File.expects(:lstat).with("/my/file").once.returns @stat - @cacher.should be_directory_exist("/my/file") - @cacher.should be_directory_exist("/my/file") - end - - it "should not consider a file to be a directory based on cached data" do - @stat.stubs(:directory?).returns false - File.stubs(:lstat).with("/my/file").returns @stat - @cacher.file_exist?("/my/file") - @cacher.should_not be_directory_exist("/my/file") - end - - it "should share cached data across autoload instances" do - File.expects(:lstat).with("/my/file").once.returns @stat - other = Puppet::Util::Autoload.new("bar", "tmp") - - @cacher.should be_directory_exist("/my/file") - other.should be_directory_exist("/my/file") - end - end -end diff --git a/spec/unit/util/autoload_spec.rb b/spec/unit/util/autoload_spec.rb new file mode 100755 index 000000000..0f73a73d7 --- /dev/null +++ b/spec/unit/util/autoload_spec.rb @@ -0,0 +1,120 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/autoload' + +describe Puppet::Util::Autoload do + before do + @autoload = Puppet::Util::Autoload.new("foo", "tmp") + + @autoload.stubs(:eachdir).yields "/my/dir" + end + + it "should use the Cacher module" do + Puppet::Util::Autoload.ancestors.should be_include(Puppet::Util::Cacher) + end + + describe "when building the search path" do + it "should collect all of the plugins and lib directories that exist in the current environment's module path" do + Puppet.settings.expects(:value).with(:environment).returns "foo" + Puppet.settings.expects(:value).with(:modulepath, :foo).returns "/a:/b:/c" + Dir.expects(:entries).with("/a").returns %w{one two} + Dir.expects(:entries).with("/b").returns %w{one two} + + FileTest.stubs(:directory?).returns false + FileTest.expects(:directory?).with("/a").returns true + FileTest.expects(:directory?).with("/b").returns true + %w{/a/one/plugins /a/two/lib /b/one/plugins /b/two/lib}.each do |d| + FileTest.expects(:directory?).with(d).returns true + end + + @autoload.module_directories.should == %w{/a/one/plugins /a/two/lib /b/one/plugins /b/two/lib} + end + + it "should not look for lib directories in directories starting with '.'" do + Puppet.settings.expects(:value).with(:environment).returns "foo" + Puppet.settings.expects(:value).with(:modulepath, :foo).returns "/a" + Dir.expects(:entries).with("/a").returns %w{. ..} + + FileTest.expects(:directory?).with("/a").returns true + FileTest.expects(:directory?).with("/a/./lib").never + FileTest.expects(:directory?).with("/a/./plugins").never + FileTest.expects(:directory?).with("/a/../lib").never + FileTest.expects(:directory?).with("/a/../plugins").never + + @autoload.module_directories + end + + it "should include the module directories, the Puppet libdir, and all of the Ruby load directories" do + Puppet.stubs(:[]).with(:libdir).returns(%w{/libdir1 /lib/dir/two /third/lib/dir}.join(File::PATH_SEPARATOR)) + @autoload.expects(:module_directories).returns %w{/one /two} + @autoload.search_directories.should == %w{/one /two /libdir1 /lib/dir/two /third/lib/dir} + $: + end + + it "should include in its search path all of the search directories that have a subdirectory matching the autoload path" do + @autoload = Puppet::Util::Autoload.new("foo", "loaddir") + @autoload.expects(:search_directories).returns %w{/one /two /three} + FileTest.expects(:directory?).with("/one/loaddir").returns true + FileTest.expects(:directory?).with("/two/loaddir").returns false + FileTest.expects(:directory?).with("/three/loaddir").returns true + @autoload.searchpath.should == ["/one/loaddir", "/three/loaddir"] + end + end + + it "should include its FileCache module" do + Puppet::Util::Autoload.ancestors.should be_include(Puppet::Util::Autoload::FileCache) + end + + describe "when loading a file" do + before do + @autoload.stubs(:searchpath).returns %w{/a} + end + + [RuntimeError, LoadError, SyntaxError].each do |error| + it "should die with Puppet::Error if a #{error.to_s} exception is thrown" do + @autoload.stubs(:file_exist?).returns true + + Kernel.expects(:load).raises error + + lambda { @autoload.load("foo") }.should raise_error(Puppet::Error) + end + end + + it "should not raise an error if the file is missing" do + @autoload.load("foo").should == false + end + + it "should register loaded files with the main loaded file list so they are not reloaded by ruby" do + @autoload.stubs(:file_exist?).returns true + Kernel.stubs(:load) + + @autoload.load("myfile") + + $".should be_include("tmp/myfile.rb") + end + end + + describe "when loading all files" do + before do + @autoload.stubs(:searchpath).returns %w{/a} + Dir.stubs(:glob).returns "/path/to/file.rb" + + @autoload.class.stubs(:loaded?).returns(false) + end + + [RuntimeError, LoadError, SyntaxError].each do |error| + it "should die an if a #{error.to_s} exception is thrown" do + Kernel.expects(:require).raises error + + lambda { @autoload.loadall }.should raise_error(Puppet::Error) + end + end + + it "should require the full path to the file" do + Kernel.expects(:require).with("/path/to/file.rb") + + @autoload.loadall + end + end +end diff --git a/spec/unit/util/autoload_spec_spec.rb b/spec/unit/util/autoload_spec_spec.rb deleted file mode 100755 index 0f73a73d7..000000000 --- a/spec/unit/util/autoload_spec_spec.rb +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/autoload' - -describe Puppet::Util::Autoload do - before do - @autoload = Puppet::Util::Autoload.new("foo", "tmp") - - @autoload.stubs(:eachdir).yields "/my/dir" - end - - it "should use the Cacher module" do - Puppet::Util::Autoload.ancestors.should be_include(Puppet::Util::Cacher) - end - - describe "when building the search path" do - it "should collect all of the plugins and lib directories that exist in the current environment's module path" do - Puppet.settings.expects(:value).with(:environment).returns "foo" - Puppet.settings.expects(:value).with(:modulepath, :foo).returns "/a:/b:/c" - Dir.expects(:entries).with("/a").returns %w{one two} - Dir.expects(:entries).with("/b").returns %w{one two} - - FileTest.stubs(:directory?).returns false - FileTest.expects(:directory?).with("/a").returns true - FileTest.expects(:directory?).with("/b").returns true - %w{/a/one/plugins /a/two/lib /b/one/plugins /b/two/lib}.each do |d| - FileTest.expects(:directory?).with(d).returns true - end - - @autoload.module_directories.should == %w{/a/one/plugins /a/two/lib /b/one/plugins /b/two/lib} - end - - it "should not look for lib directories in directories starting with '.'" do - Puppet.settings.expects(:value).with(:environment).returns "foo" - Puppet.settings.expects(:value).with(:modulepath, :foo).returns "/a" - Dir.expects(:entries).with("/a").returns %w{. ..} - - FileTest.expects(:directory?).with("/a").returns true - FileTest.expects(:directory?).with("/a/./lib").never - FileTest.expects(:directory?).with("/a/./plugins").never - FileTest.expects(:directory?).with("/a/../lib").never - FileTest.expects(:directory?).with("/a/../plugins").never - - @autoload.module_directories - end - - it "should include the module directories, the Puppet libdir, and all of the Ruby load directories" do - Puppet.stubs(:[]).with(:libdir).returns(%w{/libdir1 /lib/dir/two /third/lib/dir}.join(File::PATH_SEPARATOR)) - @autoload.expects(:module_directories).returns %w{/one /two} - @autoload.search_directories.should == %w{/one /two /libdir1 /lib/dir/two /third/lib/dir} + $: - end - - it "should include in its search path all of the search directories that have a subdirectory matching the autoload path" do - @autoload = Puppet::Util::Autoload.new("foo", "loaddir") - @autoload.expects(:search_directories).returns %w{/one /two /three} - FileTest.expects(:directory?).with("/one/loaddir").returns true - FileTest.expects(:directory?).with("/two/loaddir").returns false - FileTest.expects(:directory?).with("/three/loaddir").returns true - @autoload.searchpath.should == ["/one/loaddir", "/three/loaddir"] - end - end - - it "should include its FileCache module" do - Puppet::Util::Autoload.ancestors.should be_include(Puppet::Util::Autoload::FileCache) - end - - describe "when loading a file" do - before do - @autoload.stubs(:searchpath).returns %w{/a} - end - - [RuntimeError, LoadError, SyntaxError].each do |error| - it "should die with Puppet::Error if a #{error.to_s} exception is thrown" do - @autoload.stubs(:file_exist?).returns true - - Kernel.expects(:load).raises error - - lambda { @autoload.load("foo") }.should raise_error(Puppet::Error) - end - end - - it "should not raise an error if the file is missing" do - @autoload.load("foo").should == false - end - - it "should register loaded files with the main loaded file list so they are not reloaded by ruby" do - @autoload.stubs(:file_exist?).returns true - Kernel.stubs(:load) - - @autoload.load("myfile") - - $".should be_include("tmp/myfile.rb") - end - end - - describe "when loading all files" do - before do - @autoload.stubs(:searchpath).returns %w{/a} - Dir.stubs(:glob).returns "/path/to/file.rb" - - @autoload.class.stubs(:loaded?).returns(false) - end - - [RuntimeError, LoadError, SyntaxError].each do |error| - it "should die an if a #{error.to_s} exception is thrown" do - Kernel.expects(:require).raises error - - lambda { @autoload.loadall }.should raise_error(Puppet::Error) - end - end - - it "should require the full path to the file" do - Kernel.expects(:require).with("/path/to/file.rb") - - @autoload.loadall - end - end -end diff --git a/spec/unit/util/backups_spec.rb b/spec/unit/util/backups_spec.rb new file mode 100755 index 000000000..fa6756e10 --- /dev/null +++ b/spec/unit/util/backups_spec.rb @@ -0,0 +1,159 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/backups' +include PuppetTest + +describe Puppet::Util::Backups do + before do + FileTest.stubs(:exists?).returns true + end + + describe "when backing up a file" do + it "should noop if the file does not exist" do + FileTest.expects(:exists?).returns false + file = Puppet::Type.type(:file).new(:name => '/no/such/file') + file.expects(:bucket).never + + file.perform_backup + end + + it "should succeed silently if self[:backup] is false" do + file = Puppet::Type.type(:file).new(:name => '/no/such/file', :backup => false) + file.expects(:bucket).never + FileTest.expects(:exists?).never + file.perform_backup + end + + it "a bucket should be used when provided" do + path = '/my/file' + + File.stubs(:stat).with(path).returns(mock('stat', :ftype => 'file')) + + file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') + bucket = stub('bucket', 'name' => 'foo') + file.stubs(:bucket).returns bucket + + bucket.expects(:backup).with(path).returns("mysum") + + file.perform_backup + end + + it "should propagate any exceptions encountered when backing up to a filebucket" do + path = '/my/file' + + File.stubs(:stat).with(path).returns(mock('stat', :ftype => 'file')) + + file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') + bucket = stub('bucket', 'name' => 'foo') + file.stubs(:bucket).returns bucket + + bucket.expects(:backup).raises ArgumentError + + lambda { file.perform_backup }.should raise_error(ArgumentError) + end + + describe "and no filebucket is configured" do + it "should remove any local backup if one exists" do + path = '/my/file' + FileTest.stubs(:exists?).returns true + + backup = path + ".foo" + + File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file") + File.expects(:unlink).with(backup) + + FileUtils.stubs(:cp_r) + + file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') + file.perform_backup + end + + it "should fail when the old backup can't be removed" do + path = '/my/file' + FileTest.stubs(:exists?).returns true + + backup = path + ".foo" + + File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file") + File.expects(:unlink).raises ArgumentError + + FileUtils.expects(:cp_r).never + + file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') + lambda { file.perform_backup }.should raise_error(Puppet::Error) + end + + it "should not try to remove backups that don't exist" do + path = '/my/file' + FileTest.stubs(:exists?).returns true + + backup = path + ".foo" + + File.expects(:lstat).with(backup).raises(Errno::ENOENT) + File.expects(:unlink).never + + FileUtils.stubs(:cp_r) + + file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') + file.perform_backup + end + + it "a copy should be created in the local directory" do + path = '/my/file' + FileTest.stubs(:exists?).with(path).returns true + + FileUtils.expects(:cp_r).with(path, path + ".foo", :preserve => true) + + file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') + file.perform_backup.should be_true + end + + it "should propagate exceptions if no backup can be created" do + path = '/my/file' + FileTest.stubs(:exists?).with(path).returns true + + FileUtils.expects(:cp_r).raises ArgumentError + + file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') + lambda { file.perform_backup }.should raise_error(Puppet::Error) + end + end + end + + describe "when backing up a directory" do + it "a bucket should work when provided" do + path = '/my/dir' + + File.stubs(:file?).returns true + Find.expects(:find).with(path).yields("/my/dir/file") + + bucket = stub('bucket', :name => "eh") + bucket.expects(:backup).with("/my/dir/file").returns true + + file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') + file.stubs(:bucket).returns bucket + + File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory')) + + file.perform_backup + end + + it "should do nothing when recursing" do + path = '/my/dir' + + bucket = stub('bucket', :name => "eh") + bucket.expects(:backup).never + + file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo', :recurse => true) + file.stubs(:bucket).returns bucket + + File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory')) + + Find.expects(:find).never + + file.perform_backup + end + end +end diff --git a/spec/unit/util/backups_spec_spec.rb b/spec/unit/util/backups_spec_spec.rb deleted file mode 100755 index fa6756e10..000000000 --- a/spec/unit/util/backups_spec_spec.rb +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/util/backups' -include PuppetTest - -describe Puppet::Util::Backups do - before do - FileTest.stubs(:exists?).returns true - end - - describe "when backing up a file" do - it "should noop if the file does not exist" do - FileTest.expects(:exists?).returns false - file = Puppet::Type.type(:file).new(:name => '/no/such/file') - file.expects(:bucket).never - - file.perform_backup - end - - it "should succeed silently if self[:backup] is false" do - file = Puppet::Type.type(:file).new(:name => '/no/such/file', :backup => false) - file.expects(:bucket).never - FileTest.expects(:exists?).never - file.perform_backup - end - - it "a bucket should be used when provided" do - path = '/my/file' - - File.stubs(:stat).with(path).returns(mock('stat', :ftype => 'file')) - - file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') - bucket = stub('bucket', 'name' => 'foo') - file.stubs(:bucket).returns bucket - - bucket.expects(:backup).with(path).returns("mysum") - - file.perform_backup - end - - it "should propagate any exceptions encountered when backing up to a filebucket" do - path = '/my/file' - - File.stubs(:stat).with(path).returns(mock('stat', :ftype => 'file')) - - file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') - bucket = stub('bucket', 'name' => 'foo') - file.stubs(:bucket).returns bucket - - bucket.expects(:backup).raises ArgumentError - - lambda { file.perform_backup }.should raise_error(ArgumentError) - end - - describe "and no filebucket is configured" do - it "should remove any local backup if one exists" do - path = '/my/file' - FileTest.stubs(:exists?).returns true - - backup = path + ".foo" - - File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file") - File.expects(:unlink).with(backup) - - FileUtils.stubs(:cp_r) - - file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') - file.perform_backup - end - - it "should fail when the old backup can't be removed" do - path = '/my/file' - FileTest.stubs(:exists?).returns true - - backup = path + ".foo" - - File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file") - File.expects(:unlink).raises ArgumentError - - FileUtils.expects(:cp_r).never - - file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') - lambda { file.perform_backup }.should raise_error(Puppet::Error) - end - - it "should not try to remove backups that don't exist" do - path = '/my/file' - FileTest.stubs(:exists?).returns true - - backup = path + ".foo" - - File.expects(:lstat).with(backup).raises(Errno::ENOENT) - File.expects(:unlink).never - - FileUtils.stubs(:cp_r) - - file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') - file.perform_backup - end - - it "a copy should be created in the local directory" do - path = '/my/file' - FileTest.stubs(:exists?).with(path).returns true - - FileUtils.expects(:cp_r).with(path, path + ".foo", :preserve => true) - - file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') - file.perform_backup.should be_true - end - - it "should propagate exceptions if no backup can be created" do - path = '/my/file' - FileTest.stubs(:exists?).with(path).returns true - - FileUtils.expects(:cp_r).raises ArgumentError - - file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') - lambda { file.perform_backup }.should raise_error(Puppet::Error) - end - end - end - - describe "when backing up a directory" do - it "a bucket should work when provided" do - path = '/my/dir' - - File.stubs(:file?).returns true - Find.expects(:find).with(path).yields("/my/dir/file") - - bucket = stub('bucket', :name => "eh") - bucket.expects(:backup).with("/my/dir/file").returns true - - file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') - file.stubs(:bucket).returns bucket - - File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory')) - - file.perform_backup - end - - it "should do nothing when recursing" do - path = '/my/dir' - - bucket = stub('bucket', :name => "eh") - bucket.expects(:backup).never - - file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo', :recurse => true) - file.stubs(:bucket).returns bucket - - File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory')) - - Find.expects(:find).never - - file.perform_backup - end - end -end diff --git a/spec/unit/util/cache_accumulator_spec.rb b/spec/unit/util/cache_accumulator_spec.rb new file mode 100644 index 000000000..2d976d0b1 --- /dev/null +++ b/spec/unit/util/cache_accumulator_spec.rb @@ -0,0 +1,69 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/util/rails/cache_accumulator' + +describe Puppet::Util::CacheAccumulator do + before :each do + @test_class = Class.new do + attr_accessor :name + + include Puppet::Util::CacheAccumulator + accumulates :name + + def initialize(n) + self.name = n + end + end + end + + it 'should delegate to underlying find_or_create_by_* method and accumulate results' do + @test_class.expects(:find_or_create_by_name).with('foo').returns(@test_class.new('foo')).once + obj = @test_class.accumulate_by_name('foo') + obj.name.should == 'foo' + @test_class.accumulate_by_name('foo').should == obj + end + + it 'should delegate bulk lookups to find with appropriate arguments and returning result count' do + @test_class.expects(:find).with(:all, + :conditions => {:name => ['a', 'b', 'c']} + ).returns(['a','b','c'].collect {|n| @test_class.new(n)}).once + @test_class.accumulate_by_name('a', 'b', 'c').should == 3 + end + + it 'should only need find_or_create_by_name lookup for missing bulk entries' do + @test_class.expects(:find).with(:all, + :conditions => {:name => ['a', 'b']} + ).returns([ @test_class.new('a') ]).once + @test_class.expects(:find_or_create_by_name).with('b').returns(@test_class.new('b')).once + @test_class.expects(:find_or_create_by_name).with('a').never + @test_class.accumulate_by_name('a','b').should == 1 + @test_class.accumulate_by_name('a').name.should == 'a' + @test_class.accumulate_by_name('b').name.should == 'b' + end + + it 'should keep consumer classes separate' do + @alt_class = Class.new do + attr_accessor :name + + include Puppet::Util::CacheAccumulator + accumulates :name + + def initialize(n) + self.name = n + end + end + name = 'foo' + @test_class.expects(:find_or_create_by_name).with(name).returns(@test_class.new(name)).once + @alt_class.expects(:find_or_create_by_name).with(name).returns(@alt_class.new(name)).once + + [@test_class, @alt_class].each do |klass| + klass.accumulate_by_name(name).name.should == name + klass.accumulate_by_name(name).class.should == klass + end + end + + it 'should clear accumulated cache with reset_*_accumulator' do + # figure out how to test this appropriately... + end +end diff --git a/spec/unit/util/cache_accumulator_spec_spec.rb b/spec/unit/util/cache_accumulator_spec_spec.rb deleted file mode 100644 index 2d976d0b1..000000000 --- a/spec/unit/util/cache_accumulator_spec_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/util/rails/cache_accumulator' - -describe Puppet::Util::CacheAccumulator do - before :each do - @test_class = Class.new do - attr_accessor :name - - include Puppet::Util::CacheAccumulator - accumulates :name - - def initialize(n) - self.name = n - end - end - end - - it 'should delegate to underlying find_or_create_by_* method and accumulate results' do - @test_class.expects(:find_or_create_by_name).with('foo').returns(@test_class.new('foo')).once - obj = @test_class.accumulate_by_name('foo') - obj.name.should == 'foo' - @test_class.accumulate_by_name('foo').should == obj - end - - it 'should delegate bulk lookups to find with appropriate arguments and returning result count' do - @test_class.expects(:find).with(:all, - :conditions => {:name => ['a', 'b', 'c']} - ).returns(['a','b','c'].collect {|n| @test_class.new(n)}).once - @test_class.accumulate_by_name('a', 'b', 'c').should == 3 - end - - it 'should only need find_or_create_by_name lookup for missing bulk entries' do - @test_class.expects(:find).with(:all, - :conditions => {:name => ['a', 'b']} - ).returns([ @test_class.new('a') ]).once - @test_class.expects(:find_or_create_by_name).with('b').returns(@test_class.new('b')).once - @test_class.expects(:find_or_create_by_name).with('a').never - @test_class.accumulate_by_name('a','b').should == 1 - @test_class.accumulate_by_name('a').name.should == 'a' - @test_class.accumulate_by_name('b').name.should == 'b' - end - - it 'should keep consumer classes separate' do - @alt_class = Class.new do - attr_accessor :name - - include Puppet::Util::CacheAccumulator - accumulates :name - - def initialize(n) - self.name = n - end - end - name = 'foo' - @test_class.expects(:find_or_create_by_name).with(name).returns(@test_class.new(name)).once - @alt_class.expects(:find_or_create_by_name).with(name).returns(@alt_class.new(name)).once - - [@test_class, @alt_class].each do |klass| - klass.accumulate_by_name(name).name.should == name - klass.accumulate_by_name(name).class.should == klass - end - end - - it 'should clear accumulated cache with reset_*_accumulator' do - # figure out how to test this appropriately... - end -end diff --git a/spec/unit/util/cacher_spec.rb b/spec/unit/util/cacher_spec.rb new file mode 100755 index 000000000..eb8515e4d --- /dev/null +++ b/spec/unit/util/cacher_spec.rb @@ -0,0 +1,185 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/cacher' + +class ExpirerTest + include Puppet::Util::Cacher::Expirer +end + +class CacheTest + @@init_count = 0 + + include Puppet::Util::Cacher + cached_attr(:instance_cache) { Time.now } +end + +describe Puppet::Util::Cacher::Expirer do + before do + @expirer = ExpirerTest.new + end + + it "should be able to test whether a timestamp is expired" do + @expirer.should respond_to(:dependent_data_expired?) + end + + it "should be able to expire all values" do + @expirer.should respond_to(:expire) + end + + it "should consider any value to be valid if it has never been expired" do + @expirer.should_not be_dependent_data_expired(Time.now) + end + + it "should consider any value created after expiration to be expired" do + @expirer.expire + @expirer.should be_dependent_data_expired(Time.now - 1) + end +end + +describe Puppet::Util::Cacher do + it "should be extended with the Expirer module" do + Puppet::Util::Cacher.singleton_class.ancestors.should be_include(Puppet::Util::Cacher::Expirer) + end + + it "should support defining cached attributes" do + CacheTest.methods.should be_include("cached_attr") + end + + it "should default to the Cacher module as its expirer" do + CacheTest.new.expirer.should equal(Puppet::Util::Cacher) + end + + describe "when using cached attributes" do + before do + @expirer = ExpirerTest.new + @object = CacheTest.new + + @object.stubs(:expirer).returns @expirer + end + + it "should create a getter for the cached attribute" do + @object.should respond_to(:instance_cache) + end + + it "should return a value calculated from the provided block" do + time = Time.now + Time.stubs(:now).returns time + @object.instance_cache.should equal(time) + end + + it "should return the cached value from the getter every time if the value is not expired" do + @object.instance_cache.should equal(@object.instance_cache) + end + + it "should regenerate and return a new value using the provided block if the value has been expired" do + value = @object.instance_cache + @expirer.expire + @object.instance_cache.should_not equal(value) + end + + it "should be able to trigger expiration on its expirer" do + @expirer.expects(:expire) + @object.expire + end + + it "should do nothing when asked to expire when no expirer is available" do + cacher = CacheTest.new + class << cacher + def expirer + nil + end + end + lambda { cacher.expire }.should_not raise_error + end + + it "should be able to cache false values" do + @object.expects(:init_instance_cache).returns false + @object.instance_cache.should be_false + @object.instance_cache.should be_false + end + + it "should cache values again after expiration" do + @object.instance_cache + @expirer.expire + @object.instance_cache.should equal(@object.instance_cache) + end + + it "should always consider a value expired if it has no expirer" do + @object.stubs(:expirer).returns nil + @object.instance_cache.should_not equal(@object.instance_cache) + end + + it "should allow writing of the attribute" do + @object.should respond_to(:instance_cache=) + end + + it "should correctly configure timestamps for expiration when the cached attribute is written to" do + @object.instance_cache = "foo" + @expirer.expire + @object.instance_cache.should_not == "foo" + end + + it "should allow specification of a ttl for cached attributes" do + klass = Class.new do + include Puppet::Util::Cacher + end + + klass.cached_attr(:myattr, :ttl => 5) { Time.now } + + klass.attr_ttl(:myattr).should == 5 + end + + it "should allow specification of a ttl as a string" do + klass = Class.new do + include Puppet::Util::Cacher + end + + klass.cached_attr(:myattr, :ttl => "5") { Time.now } + + klass.attr_ttl(:myattr).should == 5 + end + + it "should fail helpfully if the ttl cannot be converted to an integer" do + klass = Class.new do + include Puppet::Util::Cacher + end + + lambda { klass.cached_attr(:myattr, :ttl => "yep") { Time.now } }.should raise_error(ArgumentError) + end + + it "should not check for a ttl expiration if the class does not support that method" do + klass = Class.new do + extend Puppet::Util::Cacher + end + + klass.singleton_class.cached_attr(:myattr) { "eh" } + klass.myattr + end + + it "should automatically expire cached attributes whose ttl has expired, even if no expirer is present" do + klass = Class.new do + def self.to_s + "CacheTestClass" + end + include Puppet::Util::Cacher + attr_accessor :value + end + + klass.cached_attr(:myattr, :ttl => 5) { self.value += 1; self.value } + + now = Time.now + later = Time.now + 15 + + instance = klass.new + instance.value = 0 + instance.myattr.should == 1 + + Time.expects(:now).returns later + + # This call should get the new Time value, which should expire the old value + instance.myattr.should == 2 + end + end +end diff --git a/spec/unit/util/cacher_spec_spec.rb b/spec/unit/util/cacher_spec_spec.rb deleted file mode 100755 index eb8515e4d..000000000 --- a/spec/unit/util/cacher_spec_spec.rb +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/util/cacher' - -class ExpirerTest - include Puppet::Util::Cacher::Expirer -end - -class CacheTest - @@init_count = 0 - - include Puppet::Util::Cacher - cached_attr(:instance_cache) { Time.now } -end - -describe Puppet::Util::Cacher::Expirer do - before do - @expirer = ExpirerTest.new - end - - it "should be able to test whether a timestamp is expired" do - @expirer.should respond_to(:dependent_data_expired?) - end - - it "should be able to expire all values" do - @expirer.should respond_to(:expire) - end - - it "should consider any value to be valid if it has never been expired" do - @expirer.should_not be_dependent_data_expired(Time.now) - end - - it "should consider any value created after expiration to be expired" do - @expirer.expire - @expirer.should be_dependent_data_expired(Time.now - 1) - end -end - -describe Puppet::Util::Cacher do - it "should be extended with the Expirer module" do - Puppet::Util::Cacher.singleton_class.ancestors.should be_include(Puppet::Util::Cacher::Expirer) - end - - it "should support defining cached attributes" do - CacheTest.methods.should be_include("cached_attr") - end - - it "should default to the Cacher module as its expirer" do - CacheTest.new.expirer.should equal(Puppet::Util::Cacher) - end - - describe "when using cached attributes" do - before do - @expirer = ExpirerTest.new - @object = CacheTest.new - - @object.stubs(:expirer).returns @expirer - end - - it "should create a getter for the cached attribute" do - @object.should respond_to(:instance_cache) - end - - it "should return a value calculated from the provided block" do - time = Time.now - Time.stubs(:now).returns time - @object.instance_cache.should equal(time) - end - - it "should return the cached value from the getter every time if the value is not expired" do - @object.instance_cache.should equal(@object.instance_cache) - end - - it "should regenerate and return a new value using the provided block if the value has been expired" do - value = @object.instance_cache - @expirer.expire - @object.instance_cache.should_not equal(value) - end - - it "should be able to trigger expiration on its expirer" do - @expirer.expects(:expire) - @object.expire - end - - it "should do nothing when asked to expire when no expirer is available" do - cacher = CacheTest.new - class << cacher - def expirer - nil - end - end - lambda { cacher.expire }.should_not raise_error - end - - it "should be able to cache false values" do - @object.expects(:init_instance_cache).returns false - @object.instance_cache.should be_false - @object.instance_cache.should be_false - end - - it "should cache values again after expiration" do - @object.instance_cache - @expirer.expire - @object.instance_cache.should equal(@object.instance_cache) - end - - it "should always consider a value expired if it has no expirer" do - @object.stubs(:expirer).returns nil - @object.instance_cache.should_not equal(@object.instance_cache) - end - - it "should allow writing of the attribute" do - @object.should respond_to(:instance_cache=) - end - - it "should correctly configure timestamps for expiration when the cached attribute is written to" do - @object.instance_cache = "foo" - @expirer.expire - @object.instance_cache.should_not == "foo" - end - - it "should allow specification of a ttl for cached attributes" do - klass = Class.new do - include Puppet::Util::Cacher - end - - klass.cached_attr(:myattr, :ttl => 5) { Time.now } - - klass.attr_ttl(:myattr).should == 5 - end - - it "should allow specification of a ttl as a string" do - klass = Class.new do - include Puppet::Util::Cacher - end - - klass.cached_attr(:myattr, :ttl => "5") { Time.now } - - klass.attr_ttl(:myattr).should == 5 - end - - it "should fail helpfully if the ttl cannot be converted to an integer" do - klass = Class.new do - include Puppet::Util::Cacher - end - - lambda { klass.cached_attr(:myattr, :ttl => "yep") { Time.now } }.should raise_error(ArgumentError) - end - - it "should not check for a ttl expiration if the class does not support that method" do - klass = Class.new do - extend Puppet::Util::Cacher - end - - klass.singleton_class.cached_attr(:myattr) { "eh" } - klass.myattr - end - - it "should automatically expire cached attributes whose ttl has expired, even if no expirer is present" do - klass = Class.new do - def self.to_s - "CacheTestClass" - end - include Puppet::Util::Cacher - attr_accessor :value - end - - klass.cached_attr(:myattr, :ttl => 5) { self.value += 1; self.value } - - now = Time.now - later = Time.now + 15 - - instance = klass.new - instance.value = 0 - instance.myattr.should == 1 - - Time.expects(:now).returns later - - # This call should get the new Time value, which should expire the old value - instance.myattr.should == 2 - end - end -end diff --git a/spec/unit/util/checksums_spec.rb b/spec/unit/util/checksums_spec.rb new file mode 100755 index 000000000..35d18633a --- /dev/null +++ b/spec/unit/util/checksums_spec.rb @@ -0,0 +1,153 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-9-22. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/checksums' + +describe Puppet::Util::Checksums do + before do + @summer = Object.new + @summer.extend(Puppet::Util::Checksums) + end + + content_sums = [:md5, :md5lite, :sha1, :sha1lite] + file_only = [:ctime, :mtime, :none] + + content_sums.each do |sumtype| + it "should be able to calculate %s sums from strings" % sumtype do + @summer.should be_respond_to(sumtype) + end + end + + [content_sums, file_only].flatten.each do |sumtype| + it "should be able to calculate %s sums from files" % sumtype do + @summer.should be_respond_to(sumtype.to_s + "_file") + end + end + + [content_sums, file_only].flatten.each do |sumtype| + it "should be able to calculate %s sums from stream" % sumtype do + @summer.should be_respond_to(sumtype.to_s + "_stream") + end + end + + it "should have a method for determining whether a given string is a checksum" do + @summer.should respond_to(:checksum?) + end + + %w{{md5}asdfasdf {sha1}asdfasdf {ctime}asdasdf {mtime}asdfasdf}.each do |sum| + it "should consider #{sum} to be a checksum" do + @summer.should be_checksum(sum) + end + end + + %w{{nosuchsum}asdfasdf {a}asdfasdf {ctime}}.each do |sum| + it "should not consider #{sum} to be a checksum" do + @summer.should_not be_checksum(sum) + end + end + + it "should have a method for stripping a sum type from an existing checksum" do + @summer.sumtype("{md5}asdfasdfa").should == "md5" + end + + it "should have a method for stripping the data from a checksum" do + @summer.sumdata("{md5}asdfasdfa").should == "asdfasdfa" + end + + it "should return a nil sumtype if the checksum does not mention a checksum type" do + @summer.sumtype("asdfasdfa").should be_nil + end + + {:md5 => Digest::MD5, :sha1 => Digest::SHA1}.each do |sum, klass| + describe("when using %s" % sum) do + it "should use #{klass} to calculate string checksums" do + klass.expects(:hexdigest).with("mycontent").returns "whatever" + @summer.send(sum, "mycontent").should == "whatever" + end + + it "should use incremental #{klass} sums to calculate file checksums" do + digest = mock 'digest' + klass.expects(:new).returns digest + + file = "/path/to/my/file" + + fh = mock 'filehandle' + fh.expects(:read).with(4096).times(3).returns("firstline").then.returns("secondline").then.returns(nil) + #fh.expects(:read).with(512).returns("secondline") + #fh.expects(:read).with(512).returns(nil) + + File.expects(:open).with(file, "r").yields(fh) + + digest.expects(:<<).with "firstline" + digest.expects(:<<).with "secondline" + digest.expects(:hexdigest).returns :mydigest + + @summer.send(sum.to_s + "_file", file).should == :mydigest + end + + it "should yield #{klass} to the given block to calculate stream checksums" do + digest = mock 'digest' + klass.expects(:new).returns digest + digest.expects(:hexdigest).returns :mydigest + + @summer.send(sum.to_s + "_stream") do |sum| + sum.should == digest + end.should == :mydigest + end + end + end + + {:md5lite => Digest::MD5, :sha1lite => Digest::SHA1}.each do |sum, klass| + describe("when using %s" % sum) do + it "should use #{klass} to calculate string checksums from the first 512 characters of the string" do + content = "this is a test" * 100 + klass.expects(:hexdigest).with(content[0..511]).returns "whatever" + @summer.send(sum, content).should == "whatever" + end + + it "should use #{klass} to calculate a sum from the first 512 characters in the file" do + digest = mock 'digest' + klass.expects(:new).returns digest + + file = "/path/to/my/file" + + fh = mock 'filehandle' + fh.expects(:read).with(512).returns('my content') + + File.expects(:open).with(file, "r").yields(fh) + + digest.expects(:<<).with "my content" + digest.expects(:hexdigest).returns :mydigest + + @summer.send(sum.to_s + "_file", file).should == :mydigest + end + end + end + + [:ctime, :mtime].each do |sum| + describe("when using %s" % sum) do + it "should use the '#{sum}' on the file to determine the ctime" do + file = "/my/file" + stat = mock 'stat', sum => "mysum" + + File.expects(:stat).with(file).returns(stat) + + @summer.send(sum.to_s + "_file", file).should == "mysum" + end + + it "should return nil for streams" do + @summer.send(sum.to_s + "_stream").should be_nil + end + end + end + + describe "when using the none checksum" do + it "should return an empty string" do + @summer.none_file("/my/file").should == "" + end + end +end diff --git a/spec/unit/util/checksums_spec_spec.rb b/spec/unit/util/checksums_spec_spec.rb deleted file mode 100755 index 35d18633a..000000000 --- a/spec/unit/util/checksums_spec_spec.rb +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-9-22. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/util/checksums' - -describe Puppet::Util::Checksums do - before do - @summer = Object.new - @summer.extend(Puppet::Util::Checksums) - end - - content_sums = [:md5, :md5lite, :sha1, :sha1lite] - file_only = [:ctime, :mtime, :none] - - content_sums.each do |sumtype| - it "should be able to calculate %s sums from strings" % sumtype do - @summer.should be_respond_to(sumtype) - end - end - - [content_sums, file_only].flatten.each do |sumtype| - it "should be able to calculate %s sums from files" % sumtype do - @summer.should be_respond_to(sumtype.to_s + "_file") - end - end - - [content_sums, file_only].flatten.each do |sumtype| - it "should be able to calculate %s sums from stream" % sumtype do - @summer.should be_respond_to(sumtype.to_s + "_stream") - end - end - - it "should have a method for determining whether a given string is a checksum" do - @summer.should respond_to(:checksum?) - end - - %w{{md5}asdfasdf {sha1}asdfasdf {ctime}asdasdf {mtime}asdfasdf}.each do |sum| - it "should consider #{sum} to be a checksum" do - @summer.should be_checksum(sum) - end - end - - %w{{nosuchsum}asdfasdf {a}asdfasdf {ctime}}.each do |sum| - it "should not consider #{sum} to be a checksum" do - @summer.should_not be_checksum(sum) - end - end - - it "should have a method for stripping a sum type from an existing checksum" do - @summer.sumtype("{md5}asdfasdfa").should == "md5" - end - - it "should have a method for stripping the data from a checksum" do - @summer.sumdata("{md5}asdfasdfa").should == "asdfasdfa" - end - - it "should return a nil sumtype if the checksum does not mention a checksum type" do - @summer.sumtype("asdfasdfa").should be_nil - end - - {:md5 => Digest::MD5, :sha1 => Digest::SHA1}.each do |sum, klass| - describe("when using %s" % sum) do - it "should use #{klass} to calculate string checksums" do - klass.expects(:hexdigest).with("mycontent").returns "whatever" - @summer.send(sum, "mycontent").should == "whatever" - end - - it "should use incremental #{klass} sums to calculate file checksums" do - digest = mock 'digest' - klass.expects(:new).returns digest - - file = "/path/to/my/file" - - fh = mock 'filehandle' - fh.expects(:read).with(4096).times(3).returns("firstline").then.returns("secondline").then.returns(nil) - #fh.expects(:read).with(512).returns("secondline") - #fh.expects(:read).with(512).returns(nil) - - File.expects(:open).with(file, "r").yields(fh) - - digest.expects(:<<).with "firstline" - digest.expects(:<<).with "secondline" - digest.expects(:hexdigest).returns :mydigest - - @summer.send(sum.to_s + "_file", file).should == :mydigest - end - - it "should yield #{klass} to the given block to calculate stream checksums" do - digest = mock 'digest' - klass.expects(:new).returns digest - digest.expects(:hexdigest).returns :mydigest - - @summer.send(sum.to_s + "_stream") do |sum| - sum.should == digest - end.should == :mydigest - end - end - end - - {:md5lite => Digest::MD5, :sha1lite => Digest::SHA1}.each do |sum, klass| - describe("when using %s" % sum) do - it "should use #{klass} to calculate string checksums from the first 512 characters of the string" do - content = "this is a test" * 100 - klass.expects(:hexdigest).with(content[0..511]).returns "whatever" - @summer.send(sum, content).should == "whatever" - end - - it "should use #{klass} to calculate a sum from the first 512 characters in the file" do - digest = mock 'digest' - klass.expects(:new).returns digest - - file = "/path/to/my/file" - - fh = mock 'filehandle' - fh.expects(:read).with(512).returns('my content') - - File.expects(:open).with(file, "r").yields(fh) - - digest.expects(:<<).with "my content" - digest.expects(:hexdigest).returns :mydigest - - @summer.send(sum.to_s + "_file", file).should == :mydigest - end - end - end - - [:ctime, :mtime].each do |sum| - describe("when using %s" % sum) do - it "should use the '#{sum}' on the file to determine the ctime" do - file = "/my/file" - stat = mock 'stat', sum => "mysum" - - File.expects(:stat).with(file).returns(stat) - - @summer.send(sum.to_s + "_file", file).should == "mysum" - end - - it "should return nil for streams" do - @summer.send(sum.to_s + "_stream").should be_nil - end - end - end - - describe "when using the none checksum" do - it "should return an empty string" do - @summer.none_file("/my/file").should == "" - end - end -end diff --git a/spec/unit/util/command_line_spec.rb b/spec/unit/util/command_line_spec.rb new file mode 100644 index 000000000..31b2d1b03 --- /dev/null +++ b/spec/unit/util/command_line_spec.rb @@ -0,0 +1,108 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + + +require 'puppet/util/command_line' + +describe Puppet::Util::CommandLine do + before do + @tty = stub("tty", :tty? => true ) + @pipe = stub("pipe", :tty? => false) + end + + it "should pull off the first argument if it looks like a subcommand" do + command_line = Puppet::Util::CommandLine.new("puppet", %w( client --help whatever.pp ), @tty ) + + command_line.subcommand_name.should == "client" + command_line.args.should == %w( --help whatever.pp ) + end + + it "should use 'apply' if the first argument looks like a .pp file" do + command_line = Puppet::Util::CommandLine.new("puppet", %w( whatever.pp ), @tty ) + + command_line.subcommand_name.should == "apply" + command_line.args.should == %w( whatever.pp ) + end + + it "should use 'apply' if the first argument looks like a .rb file" do + command_line = Puppet::Util::CommandLine.new("puppet", %w( whatever.rb ), @tty ) + + command_line.subcommand_name.should == "apply" + command_line.args.should == %w( whatever.rb ) + end + + it "should use 'apply' if the first argument looks like a flag" do + command_line = Puppet::Util::CommandLine.new("puppet", %w( --debug ), @tty ) + + command_line.subcommand_name.should == "apply" + command_line.args.should == %w( --debug ) + end + + it "should use 'apply' if the first argument is -" do + command_line = Puppet::Util::CommandLine.new("puppet", %w( - ), @tty ) + + command_line.subcommand_name.should == "apply" + command_line.args.should == %w( - ) + end + + it "should return nil if the first argument is --help" do + command_line = Puppet::Util::CommandLine.new("puppet", %w( --help ), @tty ) + + command_line.subcommand_name.should == nil + end + + + it "should return nil if there are no arguments on a tty" do + command_line = Puppet::Util::CommandLine.new("puppet", [], @tty ) + + command_line.subcommand_name.should == nil + command_line.args.should == [] + end + + it "should use 'apply' if there are no arguments on a pipe" do + command_line = Puppet::Util::CommandLine.new("puppet", [], @pipe ) + + command_line.subcommand_name.should == "apply" + command_line.args.should == [] + end + + it "should return the executable name if it is not puppet" do + command_line = Puppet::Util::CommandLine.new("puppetmasterd", [], @tty ) + + command_line.subcommand_name.should == "puppetmasterd" + end + + it "should translate subcommand names into their legacy equivalent" do + command_line = Puppet::Util::CommandLine.new("puppet", ["master"], @tty) + command_line.legacy_executable_name.should == "puppetmasterd" + end + + it "should leave legacy command names alone" do + command_line = Puppet::Util::CommandLine.new("puppetmasterd", [], @tty) + command_line.legacy_executable_name.should == "puppetmasterd" + end + + describe "when the subcommand is not implemented" do + it "should find and invoke an executable with a hyphenated name" do + commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'], @tty) + Puppet::Util.expects(:binary).with('puppet-whatever').returns('/dev/null/puppet-whatever') + commandline.expects(:system).with('/dev/null/puppet-whatever', 'argument') + + commandline.execute + end + + describe "and an external implementation cannot be found" do + it "should abort and show the usage message" do + commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'], @tty) + Puppet::Util.expects(:binary).with('puppet-whatever').returns(nil) + commandline.expects(:system).never + + commandline.expects(:usage_message).returns("the usage message") + commandline.expects(:abort).with{|x| x =~ /the usage message/}.raises("stubbed abort") + + lambda{ commandline.execute }.should raise_error('stubbed abort') + end + end + end +end diff --git a/spec/unit/util/command_line_spec_spec.rb b/spec/unit/util/command_line_spec_spec.rb deleted file mode 100644 index 31b2d1b03..000000000 --- a/spec/unit/util/command_line_spec_spec.rb +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - - -require 'puppet/util/command_line' - -describe Puppet::Util::CommandLine do - before do - @tty = stub("tty", :tty? => true ) - @pipe = stub("pipe", :tty? => false) - end - - it "should pull off the first argument if it looks like a subcommand" do - command_line = Puppet::Util::CommandLine.new("puppet", %w( client --help whatever.pp ), @tty ) - - command_line.subcommand_name.should == "client" - command_line.args.should == %w( --help whatever.pp ) - end - - it "should use 'apply' if the first argument looks like a .pp file" do - command_line = Puppet::Util::CommandLine.new("puppet", %w( whatever.pp ), @tty ) - - command_line.subcommand_name.should == "apply" - command_line.args.should == %w( whatever.pp ) - end - - it "should use 'apply' if the first argument looks like a .rb file" do - command_line = Puppet::Util::CommandLine.new("puppet", %w( whatever.rb ), @tty ) - - command_line.subcommand_name.should == "apply" - command_line.args.should == %w( whatever.rb ) - end - - it "should use 'apply' if the first argument looks like a flag" do - command_line = Puppet::Util::CommandLine.new("puppet", %w( --debug ), @tty ) - - command_line.subcommand_name.should == "apply" - command_line.args.should == %w( --debug ) - end - - it "should use 'apply' if the first argument is -" do - command_line = Puppet::Util::CommandLine.new("puppet", %w( - ), @tty ) - - command_line.subcommand_name.should == "apply" - command_line.args.should == %w( - ) - end - - it "should return nil if the first argument is --help" do - command_line = Puppet::Util::CommandLine.new("puppet", %w( --help ), @tty ) - - command_line.subcommand_name.should == nil - end - - - it "should return nil if there are no arguments on a tty" do - command_line = Puppet::Util::CommandLine.new("puppet", [], @tty ) - - command_line.subcommand_name.should == nil - command_line.args.should == [] - end - - it "should use 'apply' if there are no arguments on a pipe" do - command_line = Puppet::Util::CommandLine.new("puppet", [], @pipe ) - - command_line.subcommand_name.should == "apply" - command_line.args.should == [] - end - - it "should return the executable name if it is not puppet" do - command_line = Puppet::Util::CommandLine.new("puppetmasterd", [], @tty ) - - command_line.subcommand_name.should == "puppetmasterd" - end - - it "should translate subcommand names into their legacy equivalent" do - command_line = Puppet::Util::CommandLine.new("puppet", ["master"], @tty) - command_line.legacy_executable_name.should == "puppetmasterd" - end - - it "should leave legacy command names alone" do - command_line = Puppet::Util::CommandLine.new("puppetmasterd", [], @tty) - command_line.legacy_executable_name.should == "puppetmasterd" - end - - describe "when the subcommand is not implemented" do - it "should find and invoke an executable with a hyphenated name" do - commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'], @tty) - Puppet::Util.expects(:binary).with('puppet-whatever').returns('/dev/null/puppet-whatever') - commandline.expects(:system).with('/dev/null/puppet-whatever', 'argument') - - commandline.execute - end - - describe "and an external implementation cannot be found" do - it "should abort and show the usage message" do - commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'], @tty) - Puppet::Util.expects(:binary).with('puppet-whatever').returns(nil) - commandline.expects(:system).never - - commandline.expects(:usage_message).returns("the usage message") - commandline.expects(:abort).with{|x| x =~ /the usage message/}.raises("stubbed abort") - - lambda{ commandline.execute }.should raise_error('stubbed abort') - end - end - end -end diff --git a/spec/unit/util/constant_inflector_spec.rb b/spec/unit/util/constant_inflector_spec.rb new file mode 100755 index 000000000..5112e730f --- /dev/null +++ b/spec/unit/util/constant_inflector_spec.rb @@ -0,0 +1,70 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-02-12. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/constant_inflector' + +describe Puppet::Util::ConstantInflector, "when converting file names to constants" do + before do + @inflector = Object.new + @inflector.extend(Puppet::Util::ConstantInflector) + end + + it "should capitalize terms" do + @inflector.file2constant("file").should == "File" + end + + it "should switch all '/' characters to double colons" do + @inflector.file2constant("file/other").should == "File::Other" + end + + it "should remove underscores and capitalize the proceeding letter" do + @inflector.file2constant("file_other").should == "FileOther" + end + + it "should correctly replace as many underscores as exist in the file name" do + @inflector.file2constant("two_under_scores/with_some_more_underscores").should == "TwoUnderScores::WithSomeMoreUnderscores" + end + + it "should collapse multiple underscores" do + @inflector.file2constant("many___scores").should == "ManyScores" + end + + it "should correctly handle file names deeper than two directories" do + @inflector.file2constant("one_two/three_four/five_six").should == "OneTwo::ThreeFour::FiveSix" + end +end + +describe Puppet::Util::ConstantInflector, "when converting constnats to file names" do + before do + @inflector = Object.new + @inflector.extend(Puppet::Util::ConstantInflector) + end + + it "should convert them to a string if necessary" do + @inflector.constant2file(Puppet::Util::ConstantInflector).should be_instance_of(String) + end + + it "should accept string inputs" do + @inflector.constant2file("Puppet::Util::ConstantInflector").should be_instance_of(String) + end + + it "should downcase all terms" do + @inflector.constant2file("Puppet").should == "puppet" + end + + it "should convert '::' to '/'" do + @inflector.constant2file("Puppet::Util::Constant").should == "puppet/util/constant" + end + + it "should convert mid-word capitalization to an underscore" do + @inflector.constant2file("OneTwo::ThreeFour").should == "one_two/three_four" + end + + it "should correctly handle constants with more than two parts" do + @inflector.constant2file("OneTwoThree::FourFiveSixSeven").should == "one_two_three/four_five_six_seven" + end +end diff --git a/spec/unit/util/constant_inflector_spec_spec.rb b/spec/unit/util/constant_inflector_spec_spec.rb deleted file mode 100755 index 5112e730f..000000000 --- a/spec/unit/util/constant_inflector_spec_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-02-12. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/util/constant_inflector' - -describe Puppet::Util::ConstantInflector, "when converting file names to constants" do - before do - @inflector = Object.new - @inflector.extend(Puppet::Util::ConstantInflector) - end - - it "should capitalize terms" do - @inflector.file2constant("file").should == "File" - end - - it "should switch all '/' characters to double colons" do - @inflector.file2constant("file/other").should == "File::Other" - end - - it "should remove underscores and capitalize the proceeding letter" do - @inflector.file2constant("file_other").should == "FileOther" - end - - it "should correctly replace as many underscores as exist in the file name" do - @inflector.file2constant("two_under_scores/with_some_more_underscores").should == "TwoUnderScores::WithSomeMoreUnderscores" - end - - it "should collapse multiple underscores" do - @inflector.file2constant("many___scores").should == "ManyScores" - end - - it "should correctly handle file names deeper than two directories" do - @inflector.file2constant("one_two/three_four/five_six").should == "OneTwo::ThreeFour::FiveSix" - end -end - -describe Puppet::Util::ConstantInflector, "when converting constnats to file names" do - before do - @inflector = Object.new - @inflector.extend(Puppet::Util::ConstantInflector) - end - - it "should convert them to a string if necessary" do - @inflector.constant2file(Puppet::Util::ConstantInflector).should be_instance_of(String) - end - - it "should accept string inputs" do - @inflector.constant2file("Puppet::Util::ConstantInflector").should be_instance_of(String) - end - - it "should downcase all terms" do - @inflector.constant2file("Puppet").should == "puppet" - end - - it "should convert '::' to '/'" do - @inflector.constant2file("Puppet::Util::Constant").should == "puppet/util/constant" - end - - it "should convert mid-word capitalization to an underscore" do - @inflector.constant2file("OneTwo::ThreeFour").should == "one_two/three_four" - end - - it "should correctly handle constants with more than two parts" do - @inflector.constant2file("OneTwoThree::FourFiveSixSeven").should == "one_two_three/four_five_six_seven" - end -end diff --git a/spec/unit/util/errors_spec.rb b/spec/unit/util/errors_spec.rb new file mode 100755 index 000000000..e30b85db5 --- /dev/null +++ b/spec/unit/util/errors_spec.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/errors' + +class ErrorTester + include Puppet::Util::Errors + attr_accessor :line, :file +end + +describe Puppet::Util::Errors do + before do + @tester = ErrorTester.new + end + + it "should provide a 'fail' method" do + @tester.should respond_to(:fail) + end + + it "should provide a 'devfail' method" do + @tester.should respond_to(:devfail) + end + + it "should raise any provided error when failing" do + lambda { @tester.fail(Puppet::ParseError, "stuff") }.should raise_error(Puppet::ParseError) + end + + it "should default to Puppet::Error when failing" do + lambda { @tester.fail("stuff") }.should raise_error(Puppet::Error) + end + + it "should have a method for converting error context into a string" do + @tester.file = "/my/file" + @tester.line = 50 + @tester.error_context.should == " at /my/file:50" + end +end diff --git a/spec/unit/util/errors_spec_spec.rb b/spec/unit/util/errors_spec_spec.rb deleted file mode 100755 index e30b85db5..000000000 --- a/spec/unit/util/errors_spec_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/errors' - -class ErrorTester - include Puppet::Util::Errors - attr_accessor :line, :file -end - -describe Puppet::Util::Errors do - before do - @tester = ErrorTester.new - end - - it "should provide a 'fail' method" do - @tester.should respond_to(:fail) - end - - it "should provide a 'devfail' method" do - @tester.should respond_to(:devfail) - end - - it "should raise any provided error when failing" do - lambda { @tester.fail(Puppet::ParseError, "stuff") }.should raise_error(Puppet::ParseError) - end - - it "should default to Puppet::Error when failing" do - lambda { @tester.fail("stuff") }.should raise_error(Puppet::Error) - end - - it "should have a method for converting error context into a string" do - @tester.file = "/my/file" - @tester.line = 50 - @tester.error_context.should == " at /my/file:50" - end -end diff --git a/spec/unit/util/feature_spec.rb b/spec/unit/util/feature_spec.rb new file mode 100755 index 000000000..576e19bc8 --- /dev/null +++ b/spec/unit/util/feature_spec.rb @@ -0,0 +1,72 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/feature' + +describe Puppet::Util::Feature do + before do + @features = Puppet::Util::Feature.new("features") + @features.stubs(:warn) + end + + it "should consider undefined features to be absent" do + @features.should_not be_defined_feature + end + + it "should be able to add new features" do + @features.add(:myfeature) {} + @features.should respond_to(:myfeature?) + end + + it "should call associated code when loading a feature" do + $loaded_feature = false + @features.add(:myfeature) { $loaded_feature = true} + $loaded_feature.should be_true + end + + it "should consider a feature absent when the feature load fails" do + @features.add(:failer) { raise "foo" } + @features.should_not be_failer + end + + it "should consider a feature to be absent when the feature load returns false" do + @features.add(:failer) { false } + @features.should_not be_failer + end + + it "should consider a feature to be present when the feature load returns true" do + @features.add(:available) { true } + @features.should be_available + end + + it "should cache the results of a feature load" do + $loaded_feature = 0 + @features.add(:myfeature) { $loaded_feature += 1 } + @features.myfeature? + @features.myfeature? + $loaded_feature.should == 1 + end + + it "should support features with libraries" do + lambda { @features.add(:puppet, :libs => %w{puppet}) }.should_not raise_error + end + + it "should consider a feature to be present if all of its libraries are present" do + @features.add(:myfeature, :libs => %w{foo bar}) + @features.expects(:require).with("foo") + @features.expects(:require).with("bar") + + @features.should be_myfeature + end + + it "should log and consider a feature to be absent if any of its libraries are absent" do + @features.add(:myfeature, :libs => %w{foo bar}) + @features.expects(:require).with("foo").raises(LoadError) + @features.stubs(:require).with("bar") + + Puppet.expects(:debug) + + @features.should_not be_myfeature + end +end diff --git a/spec/unit/util/feature_spec_spec.rb b/spec/unit/util/feature_spec_spec.rb deleted file mode 100755 index 576e19bc8..000000000 --- a/spec/unit/util/feature_spec_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/feature' - -describe Puppet::Util::Feature do - before do - @features = Puppet::Util::Feature.new("features") - @features.stubs(:warn) - end - - it "should consider undefined features to be absent" do - @features.should_not be_defined_feature - end - - it "should be able to add new features" do - @features.add(:myfeature) {} - @features.should respond_to(:myfeature?) - end - - it "should call associated code when loading a feature" do - $loaded_feature = false - @features.add(:myfeature) { $loaded_feature = true} - $loaded_feature.should be_true - end - - it "should consider a feature absent when the feature load fails" do - @features.add(:failer) { raise "foo" } - @features.should_not be_failer - end - - it "should consider a feature to be absent when the feature load returns false" do - @features.add(:failer) { false } - @features.should_not be_failer - end - - it "should consider a feature to be present when the feature load returns true" do - @features.add(:available) { true } - @features.should be_available - end - - it "should cache the results of a feature load" do - $loaded_feature = 0 - @features.add(:myfeature) { $loaded_feature += 1 } - @features.myfeature? - @features.myfeature? - $loaded_feature.should == 1 - end - - it "should support features with libraries" do - lambda { @features.add(:puppet, :libs => %w{puppet}) }.should_not raise_error - end - - it "should consider a feature to be present if all of its libraries are present" do - @features.add(:myfeature, :libs => %w{foo bar}) - @features.expects(:require).with("foo") - @features.expects(:require).with("bar") - - @features.should be_myfeature - end - - it "should log and consider a feature to be absent if any of its libraries are absent" do - @features.add(:myfeature, :libs => %w{foo bar}) - @features.expects(:require).with("foo").raises(LoadError) - @features.stubs(:require).with("bar") - - Puppet.expects(:debug) - - @features.should_not be_myfeature - end -end diff --git a/spec/unit/util/file_locking_spec.rb b/spec/unit/util/file_locking_spec.rb new file mode 100755 index 000000000..35e7f781e --- /dev/null +++ b/spec/unit/util/file_locking_spec.rb @@ -0,0 +1,156 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/file_locking' + +class FileLocker + include Puppet::Util::FileLocking +end + +describe Puppet::Util::FileLocking do + it "should have a module method for getting a read lock on files" do + Puppet::Util::FileLocking.should respond_to(:readlock) + end + + it "should have a module method for getting a write lock on files" do + Puppet::Util::FileLocking.should respond_to(:writelock) + end + + it "should have an instance method for getting a read lock on files" do + FileLocker.new.private_methods.should be_include("readlock") + end + + it "should have an instance method for getting a write lock on files" do + FileLocker.new.private_methods.should be_include("writelock") + end + + describe "when acquiring a read lock" do + before do + File.stubs(:exists?).with('/file').returns true + File.stubs(:file?).with('/file').returns true + end + + it "should use a global shared mutex" do + @sync = mock 'sync' + @sync.expects(:synchronize).with(Sync::SH).once + Puppet::Util.expects(:sync).with('/file').returns @sync + + Puppet::Util::FileLocking.readlock '/file' + end + + it "should use a shared lock on the file" do + @sync = mock 'sync' + @sync.stubs(:synchronize).yields + Puppet::Util.expects(:sync).with('/file').returns @sync + + fh = mock 'filehandle' + File.expects(:open).with("/file").yields fh + fh.expects(:lock_shared).yields "locked_fh" + + result = nil + Puppet::Util::FileLocking.readlock('/file') { |l| result = l } + result.should == "locked_fh" + end + + it "should only work on regular files" do + File.expects(:file?).with('/file').returns false + proc { Puppet::Util::FileLocking.readlock('/file') }.should raise_error(ArgumentError) + end + + it "should create missing files" do + @sync = mock 'sync' + @sync.stubs(:synchronize).yields + Puppet::Util.expects(:sync).with('/file').returns @sync + + File.expects(:exists?).with('/file').returns false + File.expects(:open).with('/file').once + + Puppet::Util::FileLocking.readlock('/file') + end + end + + describe "when acquiring a write lock" do + before do + @sync = mock 'sync' + Puppet::Util.stubs(:sync).returns @sync + @sync.stubs(:synchronize).yields + File.stubs(:file?).with('/file').returns true + File.stubs(:exists?).with('/file').returns true + end + + it "should fail if the parent directory does not exist" do + FileTest.expects(:directory?).with("/my/dir").returns false + File.stubs(:file?).with('/my/dir/file').returns true + File.stubs(:exists?).with('/my/dir/file').returns true + + lambda { Puppet::Util::FileLocking.writelock('/my/dir/file') }.should raise_error(Puppet::DevError) + end + + it "should use a global exclusive mutex" do + sync = mock 'sync' + sync.expects(:synchronize).with(Sync::EX) + Puppet::Util.expects(:sync).with("/file").returns sync + + Puppet::Util::FileLocking.writelock '/file' + end + + it "should use any specified mode when opening the file" do + File.expects(:open).with("/file", "w", :mymode) + + Puppet::Util::FileLocking.writelock('/file', :mymode) + end + + it "should use the mode of the existing file if no mode is specified" do + File.expects(:stat).with("/file").returns(mock("stat", :mode => 0755)) + File.expects(:open).with("/file", "w", 0755) + + Puppet::Util::FileLocking.writelock('/file') + end + + it "should use 0600 as the mode if no mode is specified and the file does not exist" do + File.expects(:stat).raises(Errno::ENOENT) + File.expects(:open).with("/file", "w", 0600) + + Puppet::Util::FileLocking.writelock('/file') + end + + it "should create an exclusive file lock" do + fh = mock 'fh' + File.expects(:open).yields fh + fh.expects(:lock_exclusive) + + Puppet::Util::FileLocking.writelock('/file') + end + + it "should allow the caller to write to the locked file" do + fh = mock 'fh' + File.expects(:open).yields fh + + lfh = mock 'locked_filehandle' + fh.expects(:lock_exclusive).yields(lfh) + + lfh.expects(:print).with "foo" + + Puppet::Util::FileLocking.writelock('/file') do |f| + f.print "foo" + end + end + + it "should only work on regular files" do + File.expects(:file?).with('/file').returns false + proc { Puppet::Util::FileLocking.writelock('/file') }.should raise_error(ArgumentError) + end + + it "should create missing files" do + @sync = mock 'sync' + @sync.stubs(:synchronize).yields + Puppet::Util.expects(:sync).with('/file').returns @sync + + File.expects(:exists?).with('/file').returns false + File.expects(:open).with('/file', 'w', 0600).once + + Puppet::Util::FileLocking.writelock('/file') + end + end +end diff --git a/spec/unit/util/file_locking_spec_spec.rb b/spec/unit/util/file_locking_spec_spec.rb deleted file mode 100755 index 35e7f781e..000000000 --- a/spec/unit/util/file_locking_spec_spec.rb +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/file_locking' - -class FileLocker - include Puppet::Util::FileLocking -end - -describe Puppet::Util::FileLocking do - it "should have a module method for getting a read lock on files" do - Puppet::Util::FileLocking.should respond_to(:readlock) - end - - it "should have a module method for getting a write lock on files" do - Puppet::Util::FileLocking.should respond_to(:writelock) - end - - it "should have an instance method for getting a read lock on files" do - FileLocker.new.private_methods.should be_include("readlock") - end - - it "should have an instance method for getting a write lock on files" do - FileLocker.new.private_methods.should be_include("writelock") - end - - describe "when acquiring a read lock" do - before do - File.stubs(:exists?).with('/file').returns true - File.stubs(:file?).with('/file').returns true - end - - it "should use a global shared mutex" do - @sync = mock 'sync' - @sync.expects(:synchronize).with(Sync::SH).once - Puppet::Util.expects(:sync).with('/file').returns @sync - - Puppet::Util::FileLocking.readlock '/file' - end - - it "should use a shared lock on the file" do - @sync = mock 'sync' - @sync.stubs(:synchronize).yields - Puppet::Util.expects(:sync).with('/file').returns @sync - - fh = mock 'filehandle' - File.expects(:open).with("/file").yields fh - fh.expects(:lock_shared).yields "locked_fh" - - result = nil - Puppet::Util::FileLocking.readlock('/file') { |l| result = l } - result.should == "locked_fh" - end - - it "should only work on regular files" do - File.expects(:file?).with('/file').returns false - proc { Puppet::Util::FileLocking.readlock('/file') }.should raise_error(ArgumentError) - end - - it "should create missing files" do - @sync = mock 'sync' - @sync.stubs(:synchronize).yields - Puppet::Util.expects(:sync).with('/file').returns @sync - - File.expects(:exists?).with('/file').returns false - File.expects(:open).with('/file').once - - Puppet::Util::FileLocking.readlock('/file') - end - end - - describe "when acquiring a write lock" do - before do - @sync = mock 'sync' - Puppet::Util.stubs(:sync).returns @sync - @sync.stubs(:synchronize).yields - File.stubs(:file?).with('/file').returns true - File.stubs(:exists?).with('/file').returns true - end - - it "should fail if the parent directory does not exist" do - FileTest.expects(:directory?).with("/my/dir").returns false - File.stubs(:file?).with('/my/dir/file').returns true - File.stubs(:exists?).with('/my/dir/file').returns true - - lambda { Puppet::Util::FileLocking.writelock('/my/dir/file') }.should raise_error(Puppet::DevError) - end - - it "should use a global exclusive mutex" do - sync = mock 'sync' - sync.expects(:synchronize).with(Sync::EX) - Puppet::Util.expects(:sync).with("/file").returns sync - - Puppet::Util::FileLocking.writelock '/file' - end - - it "should use any specified mode when opening the file" do - File.expects(:open).with("/file", "w", :mymode) - - Puppet::Util::FileLocking.writelock('/file', :mymode) - end - - it "should use the mode of the existing file if no mode is specified" do - File.expects(:stat).with("/file").returns(mock("stat", :mode => 0755)) - File.expects(:open).with("/file", "w", 0755) - - Puppet::Util::FileLocking.writelock('/file') - end - - it "should use 0600 as the mode if no mode is specified and the file does not exist" do - File.expects(:stat).raises(Errno::ENOENT) - File.expects(:open).with("/file", "w", 0600) - - Puppet::Util::FileLocking.writelock('/file') - end - - it "should create an exclusive file lock" do - fh = mock 'fh' - File.expects(:open).yields fh - fh.expects(:lock_exclusive) - - Puppet::Util::FileLocking.writelock('/file') - end - - it "should allow the caller to write to the locked file" do - fh = mock 'fh' - File.expects(:open).yields fh - - lfh = mock 'locked_filehandle' - fh.expects(:lock_exclusive).yields(lfh) - - lfh.expects(:print).with "foo" - - Puppet::Util::FileLocking.writelock('/file') do |f| - f.print "foo" - end - end - - it "should only work on regular files" do - File.expects(:file?).with('/file').returns false - proc { Puppet::Util::FileLocking.writelock('/file') }.should raise_error(ArgumentError) - end - - it "should create missing files" do - @sync = mock 'sync' - @sync.stubs(:synchronize).yields - Puppet::Util.expects(:sync).with('/file').returns @sync - - File.expects(:exists?).with('/file').returns false - File.expects(:open).with('/file', 'w', 0600).once - - Puppet::Util::FileLocking.writelock('/file') - end - end -end diff --git a/spec/unit/util/filetype_spec.rb b/spec/unit/util/filetype_spec.rb new file mode 100644 index 000000000..cdc1b3fe2 --- /dev/null +++ b/spec/unit/util/filetype_spec.rb @@ -0,0 +1,100 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/filetype' + +# XXX Import all of the tests into this file. +describe Puppet::Util::FileType do + describe "when backing up a file" do + before do + @file = Puppet::Util::FileType.filetype(:flat).new("/my/file") + end + + it "should do nothing if the file does not exist" do + File.expects(:exists?).with("/my/file").returns false + @file.expects(:bucket).never + @file.backup + end + + it "should use its filebucket to backup the file if it exists" do + File.expects(:exists?).with("/my/file").returns true + + bucket = mock 'bucket' + bucket.expects(:backup).with("/my/file") + + @file.expects(:bucket).returns bucket + @file.backup + end + + it "should use the default filebucket" do + bucket = mock 'bucket' + bucket.expects(:bucket).returns "mybucket" + + Puppet::Type.type(:filebucket).expects(:mkdefaultbucket).returns bucket + + @file.bucket.should == "mybucket" + end + end + + describe "the flat filetype" do + before do + @type = Puppet::Util::FileType.filetype(:flat) + end + it "should exist" do + @type.should_not be_nil + end + + describe "when the file already exists" do + it "should return the file's contents when asked to read it" do + file = @type.new("/my/file") + File.expects(:exist?).with("/my/file").returns true + File.expects(:read).with("/my/file").returns "my text" + + file.read.should == "my text" + end + + it "should unlink the file when asked to remove it" do + file = @type.new("/my/file") + File.expects(:exist?).with("/my/file").returns true + File.expects(:unlink).with("/my/file") + + file.remove + end + end + + describe "when the file does not exist" do + it "should return an empty string when asked to read the file" do + file = @type.new("/my/file") + File.expects(:exist?).with("/my/file").returns false + + file.read.should == "" + end + end + + describe "when writing the file" do + before do + @file = @type.new("/my/file") + FileUtils.stubs(:cp) + + @tempfile = stub 'tempfile', :print => nil, :close => nil, :flush => nil, :path => "/other/file" + Tempfile.stubs(:new).returns @tempfile + end + + it "should first create a temp file and copy its contents over to the file location" do + Tempfile.expects(:new).with("puppet").returns @tempfile + @tempfile.expects(:print).with("my text") + @tempfile.expects(:flush) + @tempfile.expects(:close) + FileUtils.expects(:cp).with(@tempfile.path, "/my/file") + + @file.write "my text" + end + + it "should set the selinux default context on the file" do + @file.expects(:set_selinux_default_context).with("/my/file") + @file.write "eh" + end + end + end +end diff --git a/spec/unit/util/filetype_spec_spec.rb b/spec/unit/util/filetype_spec_spec.rb deleted file mode 100644 index cdc1b3fe2..000000000 --- a/spec/unit/util/filetype_spec_spec.rb +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/filetype' - -# XXX Import all of the tests into this file. -describe Puppet::Util::FileType do - describe "when backing up a file" do - before do - @file = Puppet::Util::FileType.filetype(:flat).new("/my/file") - end - - it "should do nothing if the file does not exist" do - File.expects(:exists?).with("/my/file").returns false - @file.expects(:bucket).never - @file.backup - end - - it "should use its filebucket to backup the file if it exists" do - File.expects(:exists?).with("/my/file").returns true - - bucket = mock 'bucket' - bucket.expects(:backup).with("/my/file") - - @file.expects(:bucket).returns bucket - @file.backup - end - - it "should use the default filebucket" do - bucket = mock 'bucket' - bucket.expects(:bucket).returns "mybucket" - - Puppet::Type.type(:filebucket).expects(:mkdefaultbucket).returns bucket - - @file.bucket.should == "mybucket" - end - end - - describe "the flat filetype" do - before do - @type = Puppet::Util::FileType.filetype(:flat) - end - it "should exist" do - @type.should_not be_nil - end - - describe "when the file already exists" do - it "should return the file's contents when asked to read it" do - file = @type.new("/my/file") - File.expects(:exist?).with("/my/file").returns true - File.expects(:read).with("/my/file").returns "my text" - - file.read.should == "my text" - end - - it "should unlink the file when asked to remove it" do - file = @type.new("/my/file") - File.expects(:exist?).with("/my/file").returns true - File.expects(:unlink).with("/my/file") - - file.remove - end - end - - describe "when the file does not exist" do - it "should return an empty string when asked to read the file" do - file = @type.new("/my/file") - File.expects(:exist?).with("/my/file").returns false - - file.read.should == "" - end - end - - describe "when writing the file" do - before do - @file = @type.new("/my/file") - FileUtils.stubs(:cp) - - @tempfile = stub 'tempfile', :print => nil, :close => nil, :flush => nil, :path => "/other/file" - Tempfile.stubs(:new).returns @tempfile - end - - it "should first create a temp file and copy its contents over to the file location" do - Tempfile.expects(:new).with("puppet").returns @tempfile - @tempfile.expects(:print).with("my text") - @tempfile.expects(:flush) - @tempfile.expects(:close) - FileUtils.expects(:cp).with(@tempfile.path, "/my/file") - - @file.write "my text" - end - - it "should set the selinux default context on the file" do - @file.expects(:set_selinux_default_context).with("/my/file") - @file.write "eh" - end - end - end -end diff --git a/spec/unit/util/inline_docs_spec.rb b/spec/unit/util/inline_docs_spec.rb new file mode 100755 index 000000000..a03939ee1 --- /dev/null +++ b/spec/unit/util/inline_docs_spec.rb @@ -0,0 +1,32 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/inline_docs' + +class InlineDoccer + include Puppet::Util::InlineDocs +end + +describe Puppet::Util::InlineDocs do + describe "when included" do + it "should create a class method for specifying that docs should be associated" do + InlineDoccer.expects(:use_docs=).with true + InlineDoccer.associates_doc + end + + it "should default to not associating docs" do + (!! InlineDoccer.use_docs).should be_false + end + + it "should create an instance method for setting documentation" do + instance = InlineDoccer.new + instance.doc = "foo" + instance.doc.should == "foo" + end + + it "should default to an empty string for docs" do + InlineDoccer.new.doc.should == "" + end + end +end diff --git a/spec/unit/util/inline_docs_spec_spec.rb b/spec/unit/util/inline_docs_spec_spec.rb deleted file mode 100755 index a03939ee1..000000000 --- a/spec/unit/util/inline_docs_spec_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/inline_docs' - -class InlineDoccer - include Puppet::Util::InlineDocs -end - -describe Puppet::Util::InlineDocs do - describe "when included" do - it "should create a class method for specifying that docs should be associated" do - InlineDoccer.expects(:use_docs=).with true - InlineDoccer.associates_doc - end - - it "should default to not associating docs" do - (!! InlineDoccer.use_docs).should be_false - end - - it "should create an instance method for setting documentation" do - instance = InlineDoccer.new - instance.doc = "foo" - instance.doc.should == "foo" - end - - it "should default to an empty string for docs" do - InlineDoccer.new.doc.should == "" - end - end -end diff --git a/spec/unit/util/json_spec.rb b/spec/unit/util/json_spec.rb new file mode 100755 index 000000000..ce9648616 --- /dev/null +++ b/spec/unit/util/json_spec.rb @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/pson' + +class PsonUtil + include Puppet::Util::Pson +end + +describe Puppet::Util::Pson do + it "should fail if no data is provided" do + lambda { PsonUtil.new.pson_create("type" => "foo") }.should raise_error(ArgumentError) + end + + it "should call 'from_pson' with the provided data" do + pson = PsonUtil.new + pson.expects(:from_pson).with("mydata") + pson.pson_create("type" => "foo", "data" => "mydata") + end +end diff --git a/spec/unit/util/json_spec_spec.rb b/spec/unit/util/json_spec_spec.rb deleted file mode 100755 index ce9648616..000000000 --- a/spec/unit/util/json_spec_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/pson' - -class PsonUtil - include Puppet::Util::Pson -end - -describe Puppet::Util::Pson do - it "should fail if no data is provided" do - lambda { PsonUtil.new.pson_create("type" => "foo") }.should raise_error(ArgumentError) - end - - it "should call 'from_pson' with the provided data" do - pson = PsonUtil.new - pson.expects(:from_pson).with("mydata") - pson.pson_create("type" => "foo", "data" => "mydata") - end -end diff --git a/spec/unit/util/ldap/connection_spec.rb b/spec/unit/util/ldap/connection_spec.rb new file mode 100755 index 000000000..bead64d01 --- /dev/null +++ b/spec/unit/util/ldap/connection_spec.rb @@ -0,0 +1,169 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-19. +# Copyright (c) 2006. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/util/ldap/connection' + +# So our mocks and such all work, even when ldap isn't available. +unless Puppet.features.ldap? + class LDAP + class Conn + def initialize(*args) + end + end + class SSLConn < Conn; end + + LDAP_OPT_PROTOCOL_VERSION = 1 + LDAP_OPT_REFERRALS = 2 + LDAP_OPT_ON = 3 + end +end + +describe Puppet::Util::Ldap::Connection do + before do + Puppet.features.stubs(:ldap?).returns true + + @ldapconn = mock 'ldap' + LDAP::Conn.stubs(:new).returns(@ldapconn) + LDAP::SSLConn.stubs(:new).returns(@ldapconn) + + @ldapconn.stub_everything + + @connection = Puppet::Util::Ldap::Connection.new("host", "port") + end + + + describe "when creating connections" do + it "should require the host and port" do + lambda { Puppet::Util::Ldap::Connection.new("myhost") }.should raise_error(ArgumentError) + end + + it "should allow specification of a user and password" do + lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :user => "blah", :password => "boo") }.should_not raise_error + end + + it "should allow specification of ssl" do + lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :ssl => :tsl) }.should_not raise_error + end + + it "should support requiring a new connection" do + lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :reset => true) }.should_not raise_error + end + + it "should fail if ldap is unavailable" do + Puppet.features.expects(:ldap?).returns(false) + + lambda { Puppet::Util::Ldap::Connection.new("host", "port") }.should raise_error(Puppet::Error) + end + + it "should use neither ssl nor tls by default" do + LDAP::Conn.expects(:new).with("host", "port").returns(@ldapconn) + + @connection.start + end + + it "should use LDAP::SSLConn if ssl is requested" do + LDAP::SSLConn.expects(:new).with("host", "port").returns(@ldapconn) + + @connection.ssl = true + + @connection.start + end + + it "should use LDAP::SSLConn and tls if tls is requested" do + LDAP::SSLConn.expects(:new).with("host", "port", true).returns(@ldapconn) + + @connection.ssl = :tls + + @connection.start + end + + it "should set the protocol version to 3 and enable referrals" do + @ldapconn.expects(:set_option).with(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) + @ldapconn.expects(:set_option).with(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON) + @connection.start + end + + it "should bind with the provided user and password" do + @connection.user = "myuser" + @connection.password = "mypassword" + @ldapconn.expects(:simple_bind).with("myuser", "mypassword") + + @connection.start + end + + it "should bind with no user and password if none has been provided" do + @ldapconn.expects(:simple_bind).with(nil, nil) + @connection.start + end + end + + describe "when closing connections" do + it "should not close connections that are not open" do + @connection.stubs(:connection).returns(@ldapconn) + + @ldapconn.expects(:bound?).returns false + @ldapconn.expects(:unbind).never + + @connection.close + end + end + + it "should have a class-level method for creating a default connection" do + Puppet::Util::Ldap::Connection.should respond_to(:instance) + end + + describe "when creating a default connection" do + before do + Puppet.settings.stubs(:value).returns "whatever" + end + + it "should use the :ldapserver setting to determine the host" do + Puppet.settings.expects(:value).with(:ldapserver).returns "myserv" + Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| host == "myserv" } + Puppet::Util::Ldap::Connection.instance + end + + it "should use the :ldapport setting to determine the port" do + Puppet.settings.expects(:value).with(:ldapport).returns "456" + Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| port == "456" } + Puppet::Util::Ldap::Connection.instance + end + + it "should set ssl to :tls if tls is enabled" do + Puppet.settings.expects(:value).with(:ldaptls).returns true + Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == :tls } + Puppet::Util::Ldap::Connection.instance + end + + it "should set ssl to 'true' if ssl is enabled and tls is not" do + Puppet.settings.expects(:value).with(:ldaptls).returns false + Puppet.settings.expects(:value).with(:ldapssl).returns true + Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == true } + Puppet::Util::Ldap::Connection.instance + end + + it "should set ssl to false if neither ssl nor tls are enabled" do + Puppet.settings.expects(:value).with(:ldaptls).returns false + Puppet.settings.expects(:value).with(:ldapssl).returns false + Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == false } + Puppet::Util::Ldap::Connection.instance + end + + it "should set the ldapuser if one is set" do + Puppet.settings.expects(:value).with(:ldapuser).returns "foo" + Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:user] == "foo" } + Puppet::Util::Ldap::Connection.instance + end + + it "should set the ldapuser and ldappassword if both is set" do + Puppet.settings.expects(:value).with(:ldapuser).returns "foo" + Puppet.settings.expects(:value).with(:ldappassword).returns "bar" + Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:user] == "foo" and options[:password] == "bar" } + Puppet::Util::Ldap::Connection.instance + end + end +end diff --git a/spec/unit/util/ldap/connection_spec_spec.rb b/spec/unit/util/ldap/connection_spec_spec.rb deleted file mode 100755 index bead64d01..000000000 --- a/spec/unit/util/ldap/connection_spec_spec.rb +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-19. -# Copyright (c) 2006. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/util/ldap/connection' - -# So our mocks and such all work, even when ldap isn't available. -unless Puppet.features.ldap? - class LDAP - class Conn - def initialize(*args) - end - end - class SSLConn < Conn; end - - LDAP_OPT_PROTOCOL_VERSION = 1 - LDAP_OPT_REFERRALS = 2 - LDAP_OPT_ON = 3 - end -end - -describe Puppet::Util::Ldap::Connection do - before do - Puppet.features.stubs(:ldap?).returns true - - @ldapconn = mock 'ldap' - LDAP::Conn.stubs(:new).returns(@ldapconn) - LDAP::SSLConn.stubs(:new).returns(@ldapconn) - - @ldapconn.stub_everything - - @connection = Puppet::Util::Ldap::Connection.new("host", "port") - end - - - describe "when creating connections" do - it "should require the host and port" do - lambda { Puppet::Util::Ldap::Connection.new("myhost") }.should raise_error(ArgumentError) - end - - it "should allow specification of a user and password" do - lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :user => "blah", :password => "boo") }.should_not raise_error - end - - it "should allow specification of ssl" do - lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :ssl => :tsl) }.should_not raise_error - end - - it "should support requiring a new connection" do - lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :reset => true) }.should_not raise_error - end - - it "should fail if ldap is unavailable" do - Puppet.features.expects(:ldap?).returns(false) - - lambda { Puppet::Util::Ldap::Connection.new("host", "port") }.should raise_error(Puppet::Error) - end - - it "should use neither ssl nor tls by default" do - LDAP::Conn.expects(:new).with("host", "port").returns(@ldapconn) - - @connection.start - end - - it "should use LDAP::SSLConn if ssl is requested" do - LDAP::SSLConn.expects(:new).with("host", "port").returns(@ldapconn) - - @connection.ssl = true - - @connection.start - end - - it "should use LDAP::SSLConn and tls if tls is requested" do - LDAP::SSLConn.expects(:new).with("host", "port", true).returns(@ldapconn) - - @connection.ssl = :tls - - @connection.start - end - - it "should set the protocol version to 3 and enable referrals" do - @ldapconn.expects(:set_option).with(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) - @ldapconn.expects(:set_option).with(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON) - @connection.start - end - - it "should bind with the provided user and password" do - @connection.user = "myuser" - @connection.password = "mypassword" - @ldapconn.expects(:simple_bind).with("myuser", "mypassword") - - @connection.start - end - - it "should bind with no user and password if none has been provided" do - @ldapconn.expects(:simple_bind).with(nil, nil) - @connection.start - end - end - - describe "when closing connections" do - it "should not close connections that are not open" do - @connection.stubs(:connection).returns(@ldapconn) - - @ldapconn.expects(:bound?).returns false - @ldapconn.expects(:unbind).never - - @connection.close - end - end - - it "should have a class-level method for creating a default connection" do - Puppet::Util::Ldap::Connection.should respond_to(:instance) - end - - describe "when creating a default connection" do - before do - Puppet.settings.stubs(:value).returns "whatever" - end - - it "should use the :ldapserver setting to determine the host" do - Puppet.settings.expects(:value).with(:ldapserver).returns "myserv" - Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| host == "myserv" } - Puppet::Util::Ldap::Connection.instance - end - - it "should use the :ldapport setting to determine the port" do - Puppet.settings.expects(:value).with(:ldapport).returns "456" - Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| port == "456" } - Puppet::Util::Ldap::Connection.instance - end - - it "should set ssl to :tls if tls is enabled" do - Puppet.settings.expects(:value).with(:ldaptls).returns true - Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == :tls } - Puppet::Util::Ldap::Connection.instance - end - - it "should set ssl to 'true' if ssl is enabled and tls is not" do - Puppet.settings.expects(:value).with(:ldaptls).returns false - Puppet.settings.expects(:value).with(:ldapssl).returns true - Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == true } - Puppet::Util::Ldap::Connection.instance - end - - it "should set ssl to false if neither ssl nor tls are enabled" do - Puppet.settings.expects(:value).with(:ldaptls).returns false - Puppet.settings.expects(:value).with(:ldapssl).returns false - Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == false } - Puppet::Util::Ldap::Connection.instance - end - - it "should set the ldapuser if one is set" do - Puppet.settings.expects(:value).with(:ldapuser).returns "foo" - Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:user] == "foo" } - Puppet::Util::Ldap::Connection.instance - end - - it "should set the ldapuser and ldappassword if both is set" do - Puppet.settings.expects(:value).with(:ldapuser).returns "foo" - Puppet.settings.expects(:value).with(:ldappassword).returns "bar" - Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:user] == "foo" and options[:password] == "bar" } - Puppet::Util::Ldap::Connection.instance - end - end -end diff --git a/spec/unit/util/ldap/generator_spec.rb b/spec/unit/util/ldap/generator_spec.rb new file mode 100755 index 000000000..a6c69de83 --- /dev/null +++ b/spec/unit/util/ldap/generator_spec.rb @@ -0,0 +1,54 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-28. +# Copyright (c) 2008. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/util/ldap/generator' + +describe Puppet::Util::Ldap::Generator do + before do + @generator = Puppet::Util::Ldap::Generator.new(:uno) + end + + it "should require a parameter name at initialization" do + lambda { Puppet::Util::Ldap::Generator.new }.should raise_error + end + + it "should always return its name as a string" do + g = Puppet::Util::Ldap::Generator.new(:myname) + g.name.should == "myname" + end + + it "should provide a method for declaring the source parameter" do + @generator.from(:dos) + end + + it "should always return a set source as a string" do + @generator.from(:dos) + @generator.source.should == "dos" + end + + it "should return the source as nil if there is no source" do + @generator.source.should be_nil + end + + it "should return itself when declaring the source" do + @generator.from(:dos).should equal(@generator) + end + + it "should run the provided block when asked to generate the value" do + @generator.with { "yayness" } + @generator.generate().should == "yayness" + end + + it "should pass in any provided value to the block" do + @generator.with { |value| value.upcase } + @generator.generate("myval").should == "MYVAL" + end + + it "should return itself when declaring the code used for generating" do + @generator.with { |value| value.upcase }.should equal(@generator) + end +end diff --git a/spec/unit/util/ldap/generator_spec_spec.rb b/spec/unit/util/ldap/generator_spec_spec.rb deleted file mode 100755 index a6c69de83..000000000 --- a/spec/unit/util/ldap/generator_spec_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-28. -# Copyright (c) 2008. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/util/ldap/generator' - -describe Puppet::Util::Ldap::Generator do - before do - @generator = Puppet::Util::Ldap::Generator.new(:uno) - end - - it "should require a parameter name at initialization" do - lambda { Puppet::Util::Ldap::Generator.new }.should raise_error - end - - it "should always return its name as a string" do - g = Puppet::Util::Ldap::Generator.new(:myname) - g.name.should == "myname" - end - - it "should provide a method for declaring the source parameter" do - @generator.from(:dos) - end - - it "should always return a set source as a string" do - @generator.from(:dos) - @generator.source.should == "dos" - end - - it "should return the source as nil if there is no source" do - @generator.source.should be_nil - end - - it "should return itself when declaring the source" do - @generator.from(:dos).should equal(@generator) - end - - it "should run the provided block when asked to generate the value" do - @generator.with { "yayness" } - @generator.generate().should == "yayness" - end - - it "should pass in any provided value to the block" do - @generator.with { |value| value.upcase } - @generator.generate("myval").should == "MYVAL" - end - - it "should return itself when declaring the code used for generating" do - @generator.with { |value| value.upcase }.should equal(@generator) - end -end diff --git a/spec/unit/util/ldap/manager_spec.rb b/spec/unit/util/ldap/manager_spec.rb new file mode 100755 index 000000000..b18b1b933 --- /dev/null +++ b/spec/unit/util/ldap/manager_spec.rb @@ -0,0 +1,654 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-19. +# Copyright (c) 2006. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/util/ldap/manager' + +# If the ldap classes aren't available, go ahead and +# create some, so our tests will pass. +unless defined?(LDAP::Mod) + class LDAP + LDAP_MOD_ADD = :adding + LDAP_MOD_REPLACE = :replacing + LDAP_MOD_DELETE = :deleting + class ResultError < RuntimeError; end + class Mod + def initialize(*args) + end + end + end +end + +describe Puppet::Util::Ldap::Manager do + before do + @manager = Puppet::Util::Ldap::Manager.new + end + + it "should return self when specifying objectclasses" do + @manager.manages(:one, :two).should equal(@manager) + end + + it "should allow specification of what objectclasses are managed" do + @manager.manages(:one, :two).objectclasses.should == [:one, :two] + end + + it "should return self when specifying the relative base" do + @manager.at("yay").should equal(@manager) + end + + it "should allow specification of the relative base" do + @manager.at("yay").location.should == "yay" + end + + it "should return self when specifying the attribute map" do + @manager.maps(:one => :two).should equal(@manager) + end + + it "should allow specification of the rdn attribute" do + @manager.named_by(:uid).rdn.should == :uid + end + + it "should allow specification of the attribute map" do + @manager.maps(:one => :two).puppet2ldap.should == {:one => :two} + end + + it "should have a no-op 'and' method that just returns self" do + @manager.and.should equal(@manager) + end + + it "should allow specification of generated attributes" do + @manager.generates(:thing).should be_instance_of(Puppet::Util::Ldap::Generator) + end + + describe "when generating attributes" do + before do + @generator = stub 'generator', :source => "one", :name => "myparam" + + Puppet::Util::Ldap::Generator.stubs(:new).with(:myparam).returns @generator + end + + it "should create a generator to do the parameter generation" do + Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns @generator + @manager.generates(:myparam) + end + + it "should return the generator from the :generates method" do + @manager.generates(:myparam).should equal(@generator) + end + + it "should not replace already present values" do + @manager.generates(:myparam) + + attrs = {"myparam" => "testing"} + @generator.expects(:generate).never + + @manager.generate attrs + + attrs["myparam"].should == "testing" + end + + it "should look for the parameter as a string, not a symbol" do + @manager.generates(:myparam) + @generator.expects(:generate).with("yay").returns %w{double yay} + attrs = {"one" => "yay"} + @manager.generate attrs + + attrs["myparam"].should == %w{double yay} + end + + it "should fail if a source is specified and no source value is not defined" do + @manager.generates(:myparam) + lambda { @manager.generate "two" => "yay" }.should raise_error(ArgumentError) + end + + it "should use the source value to generate the new value if a source attribute is specified" do + @manager.generates(:myparam) + @generator.expects(:generate).with("yay").returns %w{double yay} + @manager.generate "one" => "yay" + end + + it "should not pass in any value if no source attribute is specified" do + @generator.stubs(:source).returns nil + @manager.generates(:myparam) + @generator.expects(:generate).with().returns %w{double yay} + @manager.generate "one" => "yay" + end + + it "should convert any results to arrays of strings if necessary" do + @generator.expects(:generate).returns :test + @manager.generates(:myparam) + + attrs = {"one" => "two"} + @manager.generate(attrs) + attrs["myparam"].should == ["test"] + end + + it "should add the result to the passed-in attribute hash" do + @generator.expects(:generate).returns %w{test} + @manager.generates(:myparam) + + attrs = {"one" => "two"} + @manager.generate(attrs) + attrs["myparam"].should == %w{test} + end + end + + it "should be considered invalid if it is missing a location" do + @manager.manages :me + @manager.maps :me => :you + @manager.should_not be_valid + end + + it "should be considered invalid if it is missing an objectclass list" do + @manager.maps :me => :you + @manager.at "ou=yayness" + @manager.should_not be_valid + end + + it "should be considered invalid if it is missing an attribute map" do + @manager.manages :me + @manager.at "ou=yayness" + @manager.should_not be_valid + end + + it "should be considered valid if it has an attribute map, location, and objectclass list" do + @manager.maps :me => :you + @manager.manages :me + @manager.at "ou=yayness" + @manager.should be_valid + end + + it "should calculate an instance's dn using the :ldapbase setting and the relative base" do + Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" + @manager.at "ou=mybase" + @manager.dn("me").should == "cn=me,ou=mybase,dc=testing" + end + + it "should use the specified rdn when calculating an instance's dn" do + Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" + @manager.named_by :uid + @manager.at "ou=mybase" + @manager.dn("me").should =~ /^uid=me/ + end + + it "should calculate its base using the :ldapbase setting and the relative base" do + Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" + @manager.at "ou=mybase" + @manager.base.should == "ou=mybase,dc=testing" + end + + describe "when generating its search filter" do + it "should using a single 'objectclass=' filter if a single objectclass is specified" do + @manager.manages("testing") + @manager.filter.should == "objectclass=testing" + end + + it "should create an LDAP AND filter if multiple objectclasses are specified" do + @manager.manages "testing", "okay", "done" + @manager.filter.should == "(&(objectclass=testing)(objectclass=okay)(objectclass=done))" + end + end + + it "should have a method for converting a Puppet attribute name to an LDAP attribute name as a string" do + @manager.maps :puppet_attr => :ldap_attr + @manager.ldap_name(:puppet_attr).should == "ldap_attr" + end + + it "should have a method for converting an LDAP attribute name to a Puppet attribute name" do + @manager.maps :puppet_attr => :ldap_attr + @manager.puppet_name(:ldap_attr).should == :puppet_attr + end + + it "should have a :create method for creating ldap entries" do + @manager.should respond_to(:create) + end + + it "should have a :delete method for deleting ldap entries" do + @manager.should respond_to(:delete) + end + + it "should have a :modify method for modifying ldap entries" do + @manager.should respond_to(:modify) + end + + it "should have a method for finding an entry by name in ldap" do + @manager.should respond_to(:find) + end + + describe "when converting ldap entries to hashes for providers" do + before do + @manager.maps :uno => :one, :dos => :two + + @result = @manager.entry2provider("dn" => ["cn=one,ou=people,dc=madstop"], "one" => ["two"], "three" => %w{four}, "objectclass" => %w{yay ness}) + end + + it "should set the name to the short portion of the dn" do + @result[:name].should == "one" + end + + it "should remove the objectclasses" do + @result["objectclass"].should be_nil + end + + it "should remove any attributes that are not mentioned in the map" do + @result["three"].should be_nil + end + + it "should rename convert to symbols all attributes to their puppet names" do + @result[:uno].should == %w{two} + end + + it "should set the value of all unset puppet attributes as :absent" do + @result[:dos].should == :absent + end + end + + describe "when using an ldap connection" do + before do + @ldapconn = mock 'ldapconn' + @conn = stub 'connection', :connection => @ldapconn, :start => nil, :close => nil + Puppet::Util::Ldap::Connection.stubs(:new).returns(@conn) + end + + it "should fail unless a block is given" do + lambda { @manager.connect }.should raise_error(ArgumentError) + end + + it "should open the connection with its server set to :ldapserver" do + Puppet.settings.stubs(:value).returns(false) + Puppet.settings.expects(:value).with(:ldapserver).returns("myserver") + Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[0] == "myserver" }.returns @conn + + @manager.connect { |c| } + end + + it "should open the connection with its port set to the :ldapport" do + Puppet.settings.stubs(:value).returns(false) + Puppet.settings.expects(:value).with(:ldapport).returns("28") + Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[1] == "28" }.returns @conn + + @manager.connect { |c| } + end + + it "should open the connection with no user if :ldapuser is not set" do + Puppet.settings.stubs(:value).returns(false) + Puppet.settings.expects(:value).with(:ldapuser).returns("") + Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:user].nil? }.returns @conn + + @manager.connect { |c| } + end + + it "should open the connection with its user set to the :ldapuser if it is set" do + Puppet.settings.stubs(:value).returns(false) + Puppet.settings.expects(:value).with(:ldapuser).returns("mypass") + Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:user] == "mypass" }.returns @conn + + @manager.connect { |c| } + end + + it "should open the connection with no password if :ldappassword is not set" do + Puppet.settings.stubs(:value).returns(false) + Puppet.settings.expects(:value).with(:ldappassword).returns("") + Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:password].nil? }.returns @conn + + @manager.connect { |c| } + end + + it "should open the connection with its password set to the :ldappassword if it is set" do + Puppet.settings.stubs(:value).returns(false) + Puppet.settings.expects(:value).with(:ldappassword).returns("mypass") + Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:password] == "mypass" }.returns @conn + + @manager.connect { |c| } + end + + it "should set ssl to :tls if ldaptls is enabled" do + Puppet.settings.stubs(:value).returns(false) + Puppet.settings.expects(:value).with(:ldaptls).returns(true) + Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == :tls }.returns @conn + + @manager.connect { |c| } + end + + it "should set ssl to true if ldapssl is enabled" do + Puppet.settings.stubs(:value).returns(false) + Puppet.settings.expects(:value).with(:ldapssl).returns(true) + Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == true }.returns @conn + + @manager.connect { |c| } + end + + it "should set ssl to false if neither ldaptls nor ldapssl is enabled" do + Puppet.settings.stubs(:value).returns(false) + Puppet.settings.expects(:value).with(:ldapssl).returns(false) + Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == false }.returns @conn + + @manager.connect { |c| } + end + + it "should open, yield, and then close the connection" do + @conn.expects(:start) + @conn.expects(:close) + Puppet::Util::Ldap::Connection.expects(:new).returns(@conn) + @ldapconn.expects(:test) + @manager.connect { |c| c.test } + end + + it "should close the connection even if there's an exception in the passed block" do + @conn.expects(:close) + lambda { @manager.connect { |c| raise ArgumentError } }.should raise_error(ArgumentError) + end + end + + describe "when using ldap" do + before do + @conn = mock 'connection' + @manager.stubs(:connect).yields @conn + @manager.stubs(:objectclasses).returns [:oc1, :oc2] + @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro + end + + describe "to create entries" do + it "should convert the first argument to its :create method to a full dn and pass the resulting argument list to its connection" do + @manager.expects(:dn).with("myname").returns "mydn" + @conn.expects(:add).with { |name, attrs| name == "mydn" } + + @manager.create("myname", {"attr" => "myattrs"}) + end + + it "should add the objectclasses to the attributes" do + @manager.expects(:dn).with("myname").returns "mydn" + @conn.expects(:add).with { |name, attrs| attrs["objectClass"].include?("oc1") and attrs["objectClass"].include?("oc2") } + + @manager.create("myname", {:one => :testing}) + end + + it "should add the rdn to the attributes" do + @manager.expects(:dn).with("myname").returns "mydn" + @conn.expects(:add).with { |name, attrs| attrs["cn"] == %w{myname} } + + @manager.create("myname", {:one => :testing}) + end + + it "should add 'top' to the objectclasses if it is not listed" do + @manager.expects(:dn).with("myname").returns "mydn" + @conn.expects(:add).with { |name, attrs| attrs["objectClass"].include?("top") } + + @manager.create("myname", {:one => :testing}) + end + + it "should add any generated values that are defined" do + generator = stub 'generator', :source => :one, :name => "myparam" + + Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns generator + + @manager.generates(:myparam) + + @manager.stubs(:dn).with("myname").returns "mydn" + + generator.expects(:generate).with(:testing).returns ["generated value"] + @conn.expects(:add).with { |name, attrs| attrs["myparam"] == ["generated value"] } + + @manager.create("myname", {:one => :testing}) + end + + it "should convert any generated values to arrays of strings if necessary" do + generator = stub 'generator', :source => :one, :name => "myparam" + + Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns generator + + @manager.generates(:myparam) + + @manager.stubs(:dn).returns "mydn" + + generator.expects(:generate).returns :generated + @conn.expects(:add).with { |name, attrs| attrs["myparam"] == ["generated"] } + + @manager.create("myname", {:one => :testing}) + end + end + + describe "do delete entries" do + it "should convert the first argument to its :delete method to a full dn and pass the resulting argument list to its connection" do + @manager.expects(:dn).with("myname").returns "mydn" + @conn.expects(:delete).with("mydn") + + @manager.delete("myname") + end + end + + describe "to modify entries" do + it "should convert the first argument to its :modify method to a full dn and pass the resulting argument list to its connection" do + @manager.expects(:dn).with("myname").returns "mydn" + @conn.expects(:modify).with("mydn", :mymods) + + @manager.modify("myname", :mymods) + end + end + + describe "to find a single entry" do + it "should use the dn of the provided name as the search base, a scope of 0, and 'objectclass=*' as the filter for a search2 call" do + @manager.expects(:dn).with("myname").returns "mydn" + @conn.expects(:search2).with("mydn", 0, "objectclass=*") + + @manager.find("myname") + end + + it "should return nil if an exception is thrown because no result is found" do + @manager.expects(:dn).with("myname").returns "mydn" + @conn.expects(:search2).raises LDAP::ResultError + + @manager.find("myname").should be_nil + end + + it "should return a converted provider hash if the result is found" do + @manager.expects(:dn).with("myname").returns "mydn" + result = {"one" => "two"} + @conn.expects(:search2).yields result + + @manager.expects(:entry2provider).with(result).returns "myprovider" + + @manager.find("myname").should == "myprovider" + end + end + + describe "to search for multiple entries" do + before do + @manager.stubs(:filter).returns "myfilter" + end + + it "should use the manager's search base as the dn of the provided name as the search base" do + @manager.expects(:base).returns "mybase" + @conn.expects(:search2).with { |base, scope, filter| base == "mybase" } + + @manager.search + end + + it "should use a scope of 1" do + @conn.expects(:search2).with { |base, scope, filter| scope == 1 } + + @manager.search + end + + it "should use any specified search filter" do + @manager.expects(:filter).never + @conn.expects(:search2).with { |base, scope, filter| filter == "boo" } + + @manager.search("boo") + end + + it "should turn its objectclass list into its search filter if one is not specified" do + @manager.expects(:filter).returns "yay" + @conn.expects(:search2).with { |base, scope, filter| filter == "yay" } + + @manager.search + end + + it "should return nil if no result is found" do + @conn.expects(:search2) + + @manager.search.should be_nil + end + + it "should return an array of the found results converted to provider hashes" do + # LAK: AFAICT, it's impossible to yield multiple times in an expectation. + one = {"dn" => "cn=one,dc=madstop,dc=com", "one" => "two"} + @conn.expects(:search2).yields(one) + + @manager.expects(:entry2provider).with(one).returns "myprov" + + @manager.search.should == ["myprov"] + end + end + end + + describe "when an instance" do + before do + @name = "myname" + @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro + end + + describe "is being updated" do + it "should get created if the current attribute list is empty and the desired attribute list has :ensure == :present" do + @manager.expects(:create) + @manager.update(@name, {}, {:ensure => :present}) + end + + it "should get created if the current attribute list has :ensure == :absent and the desired attribute list has :ensure == :present" do + @manager.expects(:create) + @manager.update(@name, {:ensure => :absent}, {:ensure => :present}) + end + + it "should get deleted if the current attribute list has :ensure == :present and the desired attribute list has :ensure == :absent" do + @manager.expects(:delete) + @manager.update(@name, {:ensure => :present}, {:ensure => :absent}) + end + + it "should get modified if both attribute lists have :ensure == :present" do + @manager.expects(:modify) + @manager.update(@name, {:ensure => :present, :one => :two}, {:ensure => :present, :one => :three}) + end + end + + describe "is being deleted" do + it "should call the :delete method with its name and manager" do + @manager.expects(:delete).with(@name) + + @manager.update(@name, {}, {:ensure => :absent}) + end + end + + describe "is being created" do + before do + @is = {} + @should = {:ensure => :present, :one => :yay, :two => :absent} + end + + it "should call the :create method with its name" do + @manager.expects(:create).with { |name, attrs| name == @name } + @manager.update(@name, @is, @should) + end + + it "should call the :create method with its property hash converted to ldap attribute names" do + @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } + @manager.update(@name, @is, @should) + end + + it "should convert the property names to strings" do + @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } + @manager.update(@name, @is, @should) + end + + it "should convert the property values to arrays if necessary" do + @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } + @manager.update(@name, @is, @should) + end + + it "should convert the property values to strings if necessary" do + @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } + @manager.update(@name, @is, @should) + end + + it "should not include :ensure in the properties sent" do + @manager.expects(:create).with { |*args| args[1][:ensure].nil? } + @manager.update(@name, @is, @should) + end + + it "should not include attributes set to :absent in the properties sent" do + @manager.expects(:create).with { |*args| args[1][:dos].nil? } + @manager.update(@name, @is, @should) + end + end + + describe "is being modified" do + it "should call the :modify method with its name and an array of LDAP::Mod instances" do + LDAP::Mod.stubs(:new).returns "whatever" + + @is = {:one => :yay} + @should = {:one => :yay, :two => :foo} + + @manager.expects(:modify).with { |name, mods| name == @name } + @manager.update(@name, @is, @should) + end + + it "should create the LDAP::Mod with the property name converted to the ldap name as a string" do + @is = {:one => :yay} + @should = {:one => :yay, :two => :foo} + mod = mock 'module' + LDAP::Mod.expects(:new).with { |form, name, value| name == "dos" }.returns mod + + @manager.stubs(:modify) + + @manager.update(@name, @is, @should) + end + + it "should create an LDAP::Mod instance of type LDAP_MOD_ADD for each attribute being added, with the attribute value converted to a string of arrays" do + @is = {:one => :yay} + @should = {:one => :yay, :two => :foo} + mod = mock 'module' + LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_ADD, "dos", ["foo"]).returns mod + + @manager.stubs(:modify) + + @manager.update(@name, @is, @should) + end + + it "should create an LDAP::Mod instance of type LDAP_MOD_DELETE for each attribute being deleted" do + @is = {:one => :yay, :two => :foo} + @should = {:one => :yay, :two => :absent} + mod = mock 'module' + LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_DELETE, "dos", []).returns mod + + @manager.stubs(:modify) + + @manager.update(@name, @is, @should) + end + + it "should create an LDAP::Mod instance of type LDAP_MOD_REPLACE for each attribute being modified, with the attribute converted to a string of arrays" do + @is = {:one => :yay, :two => :four} + @should = {:one => :yay, :two => :five} + mod = mock 'module' + LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_REPLACE, "dos", ["five"]).returns mod + + @manager.stubs(:modify) + + @manager.update(@name, @is, @should) + end + + it "should pass all created Mod instances to the modify method" do + @is = {:one => :yay, :two => :foo, :three => :absent} + @should = {:one => :yay, :two => :foe, :three => :fee, :four => :fie} + LDAP::Mod.expects(:new).times(3).returns("mod1").then.returns("mod2").then.returns("mod3") + + @manager.expects(:modify).with do |name, mods| + mods.sort == %w{mod1 mod2 mod3}.sort + end + + @manager.update(@name, @is, @should) + end + end + end +end diff --git a/spec/unit/util/ldap/manager_spec_spec.rb b/spec/unit/util/ldap/manager_spec_spec.rb deleted file mode 100755 index b18b1b933..000000000 --- a/spec/unit/util/ldap/manager_spec_spec.rb +++ /dev/null @@ -1,654 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-3-19. -# Copyright (c) 2006. All rights reserved. - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/util/ldap/manager' - -# If the ldap classes aren't available, go ahead and -# create some, so our tests will pass. -unless defined?(LDAP::Mod) - class LDAP - LDAP_MOD_ADD = :adding - LDAP_MOD_REPLACE = :replacing - LDAP_MOD_DELETE = :deleting - class ResultError < RuntimeError; end - class Mod - def initialize(*args) - end - end - end -end - -describe Puppet::Util::Ldap::Manager do - before do - @manager = Puppet::Util::Ldap::Manager.new - end - - it "should return self when specifying objectclasses" do - @manager.manages(:one, :two).should equal(@manager) - end - - it "should allow specification of what objectclasses are managed" do - @manager.manages(:one, :two).objectclasses.should == [:one, :two] - end - - it "should return self when specifying the relative base" do - @manager.at("yay").should equal(@manager) - end - - it "should allow specification of the relative base" do - @manager.at("yay").location.should == "yay" - end - - it "should return self when specifying the attribute map" do - @manager.maps(:one => :two).should equal(@manager) - end - - it "should allow specification of the rdn attribute" do - @manager.named_by(:uid).rdn.should == :uid - end - - it "should allow specification of the attribute map" do - @manager.maps(:one => :two).puppet2ldap.should == {:one => :two} - end - - it "should have a no-op 'and' method that just returns self" do - @manager.and.should equal(@manager) - end - - it "should allow specification of generated attributes" do - @manager.generates(:thing).should be_instance_of(Puppet::Util::Ldap::Generator) - end - - describe "when generating attributes" do - before do - @generator = stub 'generator', :source => "one", :name => "myparam" - - Puppet::Util::Ldap::Generator.stubs(:new).with(:myparam).returns @generator - end - - it "should create a generator to do the parameter generation" do - Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns @generator - @manager.generates(:myparam) - end - - it "should return the generator from the :generates method" do - @manager.generates(:myparam).should equal(@generator) - end - - it "should not replace already present values" do - @manager.generates(:myparam) - - attrs = {"myparam" => "testing"} - @generator.expects(:generate).never - - @manager.generate attrs - - attrs["myparam"].should == "testing" - end - - it "should look for the parameter as a string, not a symbol" do - @manager.generates(:myparam) - @generator.expects(:generate).with("yay").returns %w{double yay} - attrs = {"one" => "yay"} - @manager.generate attrs - - attrs["myparam"].should == %w{double yay} - end - - it "should fail if a source is specified and no source value is not defined" do - @manager.generates(:myparam) - lambda { @manager.generate "two" => "yay" }.should raise_error(ArgumentError) - end - - it "should use the source value to generate the new value if a source attribute is specified" do - @manager.generates(:myparam) - @generator.expects(:generate).with("yay").returns %w{double yay} - @manager.generate "one" => "yay" - end - - it "should not pass in any value if no source attribute is specified" do - @generator.stubs(:source).returns nil - @manager.generates(:myparam) - @generator.expects(:generate).with().returns %w{double yay} - @manager.generate "one" => "yay" - end - - it "should convert any results to arrays of strings if necessary" do - @generator.expects(:generate).returns :test - @manager.generates(:myparam) - - attrs = {"one" => "two"} - @manager.generate(attrs) - attrs["myparam"].should == ["test"] - end - - it "should add the result to the passed-in attribute hash" do - @generator.expects(:generate).returns %w{test} - @manager.generates(:myparam) - - attrs = {"one" => "two"} - @manager.generate(attrs) - attrs["myparam"].should == %w{test} - end - end - - it "should be considered invalid if it is missing a location" do - @manager.manages :me - @manager.maps :me => :you - @manager.should_not be_valid - end - - it "should be considered invalid if it is missing an objectclass list" do - @manager.maps :me => :you - @manager.at "ou=yayness" - @manager.should_not be_valid - end - - it "should be considered invalid if it is missing an attribute map" do - @manager.manages :me - @manager.at "ou=yayness" - @manager.should_not be_valid - end - - it "should be considered valid if it has an attribute map, location, and objectclass list" do - @manager.maps :me => :you - @manager.manages :me - @manager.at "ou=yayness" - @manager.should be_valid - end - - it "should calculate an instance's dn using the :ldapbase setting and the relative base" do - Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" - @manager.at "ou=mybase" - @manager.dn("me").should == "cn=me,ou=mybase,dc=testing" - end - - it "should use the specified rdn when calculating an instance's dn" do - Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" - @manager.named_by :uid - @manager.at "ou=mybase" - @manager.dn("me").should =~ /^uid=me/ - end - - it "should calculate its base using the :ldapbase setting and the relative base" do - Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" - @manager.at "ou=mybase" - @manager.base.should == "ou=mybase,dc=testing" - end - - describe "when generating its search filter" do - it "should using a single 'objectclass=' filter if a single objectclass is specified" do - @manager.manages("testing") - @manager.filter.should == "objectclass=testing" - end - - it "should create an LDAP AND filter if multiple objectclasses are specified" do - @manager.manages "testing", "okay", "done" - @manager.filter.should == "(&(objectclass=testing)(objectclass=okay)(objectclass=done))" - end - end - - it "should have a method for converting a Puppet attribute name to an LDAP attribute name as a string" do - @manager.maps :puppet_attr => :ldap_attr - @manager.ldap_name(:puppet_attr).should == "ldap_attr" - end - - it "should have a method for converting an LDAP attribute name to a Puppet attribute name" do - @manager.maps :puppet_attr => :ldap_attr - @manager.puppet_name(:ldap_attr).should == :puppet_attr - end - - it "should have a :create method for creating ldap entries" do - @manager.should respond_to(:create) - end - - it "should have a :delete method for deleting ldap entries" do - @manager.should respond_to(:delete) - end - - it "should have a :modify method for modifying ldap entries" do - @manager.should respond_to(:modify) - end - - it "should have a method for finding an entry by name in ldap" do - @manager.should respond_to(:find) - end - - describe "when converting ldap entries to hashes for providers" do - before do - @manager.maps :uno => :one, :dos => :two - - @result = @manager.entry2provider("dn" => ["cn=one,ou=people,dc=madstop"], "one" => ["two"], "three" => %w{four}, "objectclass" => %w{yay ness}) - end - - it "should set the name to the short portion of the dn" do - @result[:name].should == "one" - end - - it "should remove the objectclasses" do - @result["objectclass"].should be_nil - end - - it "should remove any attributes that are not mentioned in the map" do - @result["three"].should be_nil - end - - it "should rename convert to symbols all attributes to their puppet names" do - @result[:uno].should == %w{two} - end - - it "should set the value of all unset puppet attributes as :absent" do - @result[:dos].should == :absent - end - end - - describe "when using an ldap connection" do - before do - @ldapconn = mock 'ldapconn' - @conn = stub 'connection', :connection => @ldapconn, :start => nil, :close => nil - Puppet::Util::Ldap::Connection.stubs(:new).returns(@conn) - end - - it "should fail unless a block is given" do - lambda { @manager.connect }.should raise_error(ArgumentError) - end - - it "should open the connection with its server set to :ldapserver" do - Puppet.settings.stubs(:value).returns(false) - Puppet.settings.expects(:value).with(:ldapserver).returns("myserver") - Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[0] == "myserver" }.returns @conn - - @manager.connect { |c| } - end - - it "should open the connection with its port set to the :ldapport" do - Puppet.settings.stubs(:value).returns(false) - Puppet.settings.expects(:value).with(:ldapport).returns("28") - Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[1] == "28" }.returns @conn - - @manager.connect { |c| } - end - - it "should open the connection with no user if :ldapuser is not set" do - Puppet.settings.stubs(:value).returns(false) - Puppet.settings.expects(:value).with(:ldapuser).returns("") - Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:user].nil? }.returns @conn - - @manager.connect { |c| } - end - - it "should open the connection with its user set to the :ldapuser if it is set" do - Puppet.settings.stubs(:value).returns(false) - Puppet.settings.expects(:value).with(:ldapuser).returns("mypass") - Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:user] == "mypass" }.returns @conn - - @manager.connect { |c| } - end - - it "should open the connection with no password if :ldappassword is not set" do - Puppet.settings.stubs(:value).returns(false) - Puppet.settings.expects(:value).with(:ldappassword).returns("") - Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:password].nil? }.returns @conn - - @manager.connect { |c| } - end - - it "should open the connection with its password set to the :ldappassword if it is set" do - Puppet.settings.stubs(:value).returns(false) - Puppet.settings.expects(:value).with(:ldappassword).returns("mypass") - Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:password] == "mypass" }.returns @conn - - @manager.connect { |c| } - end - - it "should set ssl to :tls if ldaptls is enabled" do - Puppet.settings.stubs(:value).returns(false) - Puppet.settings.expects(:value).with(:ldaptls).returns(true) - Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == :tls }.returns @conn - - @manager.connect { |c| } - end - - it "should set ssl to true if ldapssl is enabled" do - Puppet.settings.stubs(:value).returns(false) - Puppet.settings.expects(:value).with(:ldapssl).returns(true) - Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == true }.returns @conn - - @manager.connect { |c| } - end - - it "should set ssl to false if neither ldaptls nor ldapssl is enabled" do - Puppet.settings.stubs(:value).returns(false) - Puppet.settings.expects(:value).with(:ldapssl).returns(false) - Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == false }.returns @conn - - @manager.connect { |c| } - end - - it "should open, yield, and then close the connection" do - @conn.expects(:start) - @conn.expects(:close) - Puppet::Util::Ldap::Connection.expects(:new).returns(@conn) - @ldapconn.expects(:test) - @manager.connect { |c| c.test } - end - - it "should close the connection even if there's an exception in the passed block" do - @conn.expects(:close) - lambda { @manager.connect { |c| raise ArgumentError } }.should raise_error(ArgumentError) - end - end - - describe "when using ldap" do - before do - @conn = mock 'connection' - @manager.stubs(:connect).yields @conn - @manager.stubs(:objectclasses).returns [:oc1, :oc2] - @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro - end - - describe "to create entries" do - it "should convert the first argument to its :create method to a full dn and pass the resulting argument list to its connection" do - @manager.expects(:dn).with("myname").returns "mydn" - @conn.expects(:add).with { |name, attrs| name == "mydn" } - - @manager.create("myname", {"attr" => "myattrs"}) - end - - it "should add the objectclasses to the attributes" do - @manager.expects(:dn).with("myname").returns "mydn" - @conn.expects(:add).with { |name, attrs| attrs["objectClass"].include?("oc1") and attrs["objectClass"].include?("oc2") } - - @manager.create("myname", {:one => :testing}) - end - - it "should add the rdn to the attributes" do - @manager.expects(:dn).with("myname").returns "mydn" - @conn.expects(:add).with { |name, attrs| attrs["cn"] == %w{myname} } - - @manager.create("myname", {:one => :testing}) - end - - it "should add 'top' to the objectclasses if it is not listed" do - @manager.expects(:dn).with("myname").returns "mydn" - @conn.expects(:add).with { |name, attrs| attrs["objectClass"].include?("top") } - - @manager.create("myname", {:one => :testing}) - end - - it "should add any generated values that are defined" do - generator = stub 'generator', :source => :one, :name => "myparam" - - Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns generator - - @manager.generates(:myparam) - - @manager.stubs(:dn).with("myname").returns "mydn" - - generator.expects(:generate).with(:testing).returns ["generated value"] - @conn.expects(:add).with { |name, attrs| attrs["myparam"] == ["generated value"] } - - @manager.create("myname", {:one => :testing}) - end - - it "should convert any generated values to arrays of strings if necessary" do - generator = stub 'generator', :source => :one, :name => "myparam" - - Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns generator - - @manager.generates(:myparam) - - @manager.stubs(:dn).returns "mydn" - - generator.expects(:generate).returns :generated - @conn.expects(:add).with { |name, attrs| attrs["myparam"] == ["generated"] } - - @manager.create("myname", {:one => :testing}) - end - end - - describe "do delete entries" do - it "should convert the first argument to its :delete method to a full dn and pass the resulting argument list to its connection" do - @manager.expects(:dn).with("myname").returns "mydn" - @conn.expects(:delete).with("mydn") - - @manager.delete("myname") - end - end - - describe "to modify entries" do - it "should convert the first argument to its :modify method to a full dn and pass the resulting argument list to its connection" do - @manager.expects(:dn).with("myname").returns "mydn" - @conn.expects(:modify).with("mydn", :mymods) - - @manager.modify("myname", :mymods) - end - end - - describe "to find a single entry" do - it "should use the dn of the provided name as the search base, a scope of 0, and 'objectclass=*' as the filter for a search2 call" do - @manager.expects(:dn).with("myname").returns "mydn" - @conn.expects(:search2).with("mydn", 0, "objectclass=*") - - @manager.find("myname") - end - - it "should return nil if an exception is thrown because no result is found" do - @manager.expects(:dn).with("myname").returns "mydn" - @conn.expects(:search2).raises LDAP::ResultError - - @manager.find("myname").should be_nil - end - - it "should return a converted provider hash if the result is found" do - @manager.expects(:dn).with("myname").returns "mydn" - result = {"one" => "two"} - @conn.expects(:search2).yields result - - @manager.expects(:entry2provider).with(result).returns "myprovider" - - @manager.find("myname").should == "myprovider" - end - end - - describe "to search for multiple entries" do - before do - @manager.stubs(:filter).returns "myfilter" - end - - it "should use the manager's search base as the dn of the provided name as the search base" do - @manager.expects(:base).returns "mybase" - @conn.expects(:search2).with { |base, scope, filter| base == "mybase" } - - @manager.search - end - - it "should use a scope of 1" do - @conn.expects(:search2).with { |base, scope, filter| scope == 1 } - - @manager.search - end - - it "should use any specified search filter" do - @manager.expects(:filter).never - @conn.expects(:search2).with { |base, scope, filter| filter == "boo" } - - @manager.search("boo") - end - - it "should turn its objectclass list into its search filter if one is not specified" do - @manager.expects(:filter).returns "yay" - @conn.expects(:search2).with { |base, scope, filter| filter == "yay" } - - @manager.search - end - - it "should return nil if no result is found" do - @conn.expects(:search2) - - @manager.search.should be_nil - end - - it "should return an array of the found results converted to provider hashes" do - # LAK: AFAICT, it's impossible to yield multiple times in an expectation. - one = {"dn" => "cn=one,dc=madstop,dc=com", "one" => "two"} - @conn.expects(:search2).yields(one) - - @manager.expects(:entry2provider).with(one).returns "myprov" - - @manager.search.should == ["myprov"] - end - end - end - - describe "when an instance" do - before do - @name = "myname" - @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro - end - - describe "is being updated" do - it "should get created if the current attribute list is empty and the desired attribute list has :ensure == :present" do - @manager.expects(:create) - @manager.update(@name, {}, {:ensure => :present}) - end - - it "should get created if the current attribute list has :ensure == :absent and the desired attribute list has :ensure == :present" do - @manager.expects(:create) - @manager.update(@name, {:ensure => :absent}, {:ensure => :present}) - end - - it "should get deleted if the current attribute list has :ensure == :present and the desired attribute list has :ensure == :absent" do - @manager.expects(:delete) - @manager.update(@name, {:ensure => :present}, {:ensure => :absent}) - end - - it "should get modified if both attribute lists have :ensure == :present" do - @manager.expects(:modify) - @manager.update(@name, {:ensure => :present, :one => :two}, {:ensure => :present, :one => :three}) - end - end - - describe "is being deleted" do - it "should call the :delete method with its name and manager" do - @manager.expects(:delete).with(@name) - - @manager.update(@name, {}, {:ensure => :absent}) - end - end - - describe "is being created" do - before do - @is = {} - @should = {:ensure => :present, :one => :yay, :two => :absent} - end - - it "should call the :create method with its name" do - @manager.expects(:create).with { |name, attrs| name == @name } - @manager.update(@name, @is, @should) - end - - it "should call the :create method with its property hash converted to ldap attribute names" do - @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } - @manager.update(@name, @is, @should) - end - - it "should convert the property names to strings" do - @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } - @manager.update(@name, @is, @should) - end - - it "should convert the property values to arrays if necessary" do - @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } - @manager.update(@name, @is, @should) - end - - it "should convert the property values to strings if necessary" do - @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } - @manager.update(@name, @is, @should) - end - - it "should not include :ensure in the properties sent" do - @manager.expects(:create).with { |*args| args[1][:ensure].nil? } - @manager.update(@name, @is, @should) - end - - it "should not include attributes set to :absent in the properties sent" do - @manager.expects(:create).with { |*args| args[1][:dos].nil? } - @manager.update(@name, @is, @should) - end - end - - describe "is being modified" do - it "should call the :modify method with its name and an array of LDAP::Mod instances" do - LDAP::Mod.stubs(:new).returns "whatever" - - @is = {:one => :yay} - @should = {:one => :yay, :two => :foo} - - @manager.expects(:modify).with { |name, mods| name == @name } - @manager.update(@name, @is, @should) - end - - it "should create the LDAP::Mod with the property name converted to the ldap name as a string" do - @is = {:one => :yay} - @should = {:one => :yay, :two => :foo} - mod = mock 'module' - LDAP::Mod.expects(:new).with { |form, name, value| name == "dos" }.returns mod - - @manager.stubs(:modify) - - @manager.update(@name, @is, @should) - end - - it "should create an LDAP::Mod instance of type LDAP_MOD_ADD for each attribute being added, with the attribute value converted to a string of arrays" do - @is = {:one => :yay} - @should = {:one => :yay, :two => :foo} - mod = mock 'module' - LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_ADD, "dos", ["foo"]).returns mod - - @manager.stubs(:modify) - - @manager.update(@name, @is, @should) - end - - it "should create an LDAP::Mod instance of type LDAP_MOD_DELETE for each attribute being deleted" do - @is = {:one => :yay, :two => :foo} - @should = {:one => :yay, :two => :absent} - mod = mock 'module' - LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_DELETE, "dos", []).returns mod - - @manager.stubs(:modify) - - @manager.update(@name, @is, @should) - end - - it "should create an LDAP::Mod instance of type LDAP_MOD_REPLACE for each attribute being modified, with the attribute converted to a string of arrays" do - @is = {:one => :yay, :two => :four} - @should = {:one => :yay, :two => :five} - mod = mock 'module' - LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_REPLACE, "dos", ["five"]).returns mod - - @manager.stubs(:modify) - - @manager.update(@name, @is, @should) - end - - it "should pass all created Mod instances to the modify method" do - @is = {:one => :yay, :two => :foo, :three => :absent} - @should = {:one => :yay, :two => :foe, :three => :fee, :four => :fie} - LDAP::Mod.expects(:new).times(3).returns("mod1").then.returns("mod2").then.returns("mod3") - - @manager.expects(:modify).with do |name, mods| - mods.sort == %w{mod1 mod2 mod3}.sort - end - - @manager.update(@name, @is, @should) - end - end - end -end diff --git a/spec/unit/util/loadedfile_spec.rb b/spec/unit/util/loadedfile_spec.rb new file mode 100755 index 000000000..083120e20 --- /dev/null +++ b/spec/unit/util/loadedfile_spec.rb @@ -0,0 +1,65 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'tempfile' +require 'puppet/util/loadedfile' + +describe Puppet::Util::LoadedFile do + before(:each) do + @f = Tempfile.new('loadedfile_test') + @f.puts "yayness" + @f.flush + + @loaded = Puppet::Util::LoadedFile.new(@f.path) + + fake_ctime = Time.now - (2 * Puppet[:filetimeout]) + @stat = stub('stat', :ctime => fake_ctime) + @fake_now = Time.now + (2 * Puppet[:filetimeout]) + end + + it "should recognize when the file has not changed" do + # Use fake "now" so that we can be sure changed? actually checks, without sleeping + # for Puppet[:filetimeout] seconds. + Time.stubs(:now).returns(@fake_now) + @loaded.changed?.should == false + end + + it "should recognize when the file has changed" do + # Fake File.stat so we don't have to depend on the filesystem granularity. Doing a flush() + # just didn't do the job. + File.stubs(:stat).returns(@stat) + # Use fake "now" so that we can be sure changed? actually checks, without sleeping + # for Puppet[:filetimeout] seconds. + Time.stubs(:now).returns(@fake_now) + @loaded.changed?.should be_an_instance_of(Time) + end + + it "should not catch a change until the timeout has elapsed" do + # Fake File.stat so we don't have to depend on the filesystem granularity. Doing a flush() + # just didn't do the job. + File.stubs(:stat).returns(@stat) + @loaded.changed?.should be(false) + # Use fake "now" so that we can be sure changed? actually checks, without sleeping + # for Puppet[:filetimeout] seconds. + Time.stubs(:now).returns(@fake_now) + @loaded.changed?.should_not be(false) + end + + it "should consider a file changed when that file is missing" do + @f.close! + # Use fake "now" so that we can be sure changed? actually checks, without sleeping + # for Puppet[:filetimeout] seconds. + Time.stubs(:now).returns(@fake_now) + @loaded.changed?.should_not be(false) + end + + it "should disable checking if Puppet[:filetimeout] is negative" do + Puppet[:filetimeout] = -1 + @loaded.changed?.should_not be(false) + end + + after(:each) do + @f.close + end +end diff --git a/spec/unit/util/loadedfile_spec_spec.rb b/spec/unit/util/loadedfile_spec_spec.rb deleted file mode 100755 index 083120e20..000000000 --- a/spec/unit/util/loadedfile_spec_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'tempfile' -require 'puppet/util/loadedfile' - -describe Puppet::Util::LoadedFile do - before(:each) do - @f = Tempfile.new('loadedfile_test') - @f.puts "yayness" - @f.flush - - @loaded = Puppet::Util::LoadedFile.new(@f.path) - - fake_ctime = Time.now - (2 * Puppet[:filetimeout]) - @stat = stub('stat', :ctime => fake_ctime) - @fake_now = Time.now + (2 * Puppet[:filetimeout]) - end - - it "should recognize when the file has not changed" do - # Use fake "now" so that we can be sure changed? actually checks, without sleeping - # for Puppet[:filetimeout] seconds. - Time.stubs(:now).returns(@fake_now) - @loaded.changed?.should == false - end - - it "should recognize when the file has changed" do - # Fake File.stat so we don't have to depend on the filesystem granularity. Doing a flush() - # just didn't do the job. - File.stubs(:stat).returns(@stat) - # Use fake "now" so that we can be sure changed? actually checks, without sleeping - # for Puppet[:filetimeout] seconds. - Time.stubs(:now).returns(@fake_now) - @loaded.changed?.should be_an_instance_of(Time) - end - - it "should not catch a change until the timeout has elapsed" do - # Fake File.stat so we don't have to depend on the filesystem granularity. Doing a flush() - # just didn't do the job. - File.stubs(:stat).returns(@stat) - @loaded.changed?.should be(false) - # Use fake "now" so that we can be sure changed? actually checks, without sleeping - # for Puppet[:filetimeout] seconds. - Time.stubs(:now).returns(@fake_now) - @loaded.changed?.should_not be(false) - end - - it "should consider a file changed when that file is missing" do - @f.close! - # Use fake "now" so that we can be sure changed? actually checks, without sleeping - # for Puppet[:filetimeout] seconds. - Time.stubs(:now).returns(@fake_now) - @loaded.changed?.should_not be(false) - end - - it "should disable checking if Puppet[:filetimeout] is negative" do - Puppet[:filetimeout] = -1 - @loaded.changed?.should_not be(false) - end - - after(:each) do - @f.close - end -end diff --git a/spec/unit/util/log/destinations_spec.rb b/spec/unit/util/log/destinations_spec.rb new file mode 100755 index 000000000..b43c75730 --- /dev/null +++ b/spec/unit/util/log/destinations_spec.rb @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/util/log' + +describe Puppet::Util::Log.desttypes[:report] do + before do + @dest = Puppet::Util::Log.desttypes[:report] + end + + it "should require a report at initialization" do + @dest.new("foo").report.should == "foo" + end + + it "should send new messages to the report" do + report = mock 'report' + dest = @dest.new(report) + + report.expects(:<<).with("my log") + + dest.handle "my log" + end +end diff --git a/spec/unit/util/log/destinations_spec_spec.rb b/spec/unit/util/log/destinations_spec_spec.rb deleted file mode 100755 index b43c75730..000000000 --- a/spec/unit/util/log/destinations_spec_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/util/log' - -describe Puppet::Util::Log.desttypes[:report] do - before do - @dest = Puppet::Util::Log.desttypes[:report] - end - - it "should require a report at initialization" do - @dest.new("foo").report.should == "foo" - end - - it "should send new messages to the report" do - report = mock 'report' - dest = @dest.new(report) - - report.expects(:<<).with("my log") - - dest.handle "my log" - end -end diff --git a/spec/unit/util/log_spec.rb b/spec/unit/util/log_spec.rb new file mode 100755 index 000000000..7aaa580c5 --- /dev/null +++ b/spec/unit/util/log_spec.rb @@ -0,0 +1,209 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/log' + +describe Puppet::Util::Log do + it "should be able to close all log destinations" do + Puppet::Util::Log.expects(:destinations).returns %w{foo bar} + Puppet::Util::Log.expects(:close).with("foo") + Puppet::Util::Log.expects(:close).with("bar") + + Puppet::Util::Log.close_all + end + + describe Puppet::Util::Log::DestConsole do + before do + @console = Puppet::Util::Log::DestConsole.new + end + + it "should colorize if Puppet[:color] is :ansi" do + Puppet[:color] = :ansi + + @console.colorize(:alert, "abc").should == "\e[0;31mabc\e[0m" + end + + it "should colorize if Puppet[:color] is 'yes'" do + Puppet[:color] = "yes" + + @console.colorize(:alert, "abc").should == "\e[0;31mabc\e[0m" + end + + it "should htmlize if Puppet[:color] is :html" do + Puppet[:color] = :html + + @console.colorize(:alert, "abc").should == "abc" + end + + it "should do nothing if Puppet[:color] is false" do + Puppet[:color] = false + + @console.colorize(:alert, "abc").should == "abc" + end + + it "should do nothing if Puppet[:color] is invalid" do + Puppet[:color] = "invalid option" + + @console.colorize(:alert, "abc").should == "abc" + end + end + + describe "instances" do + before do + Puppet::Util::Log.stubs(:newmessage) + end + + [:level, :message, :time, :remote].each do |attr| + it "should have a %s attribute" % attr do + log = Puppet::Util::Log.new :level => :notice, :message => "A test message" + log.should respond_to(attr) + log.should respond_to(attr.to_s + "=") + end + end + + it "should fail if created without a level" do + lambda { Puppet::Util::Log.new(:message => "A test message") }.should raise_error(ArgumentError) + end + + it "should fail if created without a message" do + lambda { Puppet::Util::Log.new(:level => :notice) }.should raise_error(ArgumentError) + end + + it "should make available the level passed in at initialization" do + Puppet::Util::Log.new(:level => :notice, :message => "A test message").level.should == :notice + end + + it "should make available the message passed in at initialization" do + Puppet::Util::Log.new(:level => :notice, :message => "A test message").message.should == "A test message" + end + + # LAK:NOTE I don't know why this behavior is here, I'm just testing what's in the code, + # at least at first. + it "should always convert messages to strings" do + Puppet::Util::Log.new(:level => :notice, :message => :foo).message.should == "foo" + end + + it "should convert the level to a symbol if it's passed in as a string" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).level.should == :notice + end + + it "should fail if the level is not a symbol or string" do + lambda { Puppet::Util::Log.new(:level => 50, :message => :foo) }.should raise_error(ArgumentError) + end + + it "should fail if the provided level is not valid" do + Puppet::Util::Log.expects(:validlevel?).with(:notice).returns false + lambda { Puppet::Util::Log.new(:level => :notice, :message => :foo) }.should raise_error(ArgumentError) + end + + it "should set its time to the initialization time" do + time = mock 'time' + Time.expects(:now).returns time + Puppet::Util::Log.new(:level => "notice", :message => :foo).time.should equal(time) + end + + it "should make available any passed-in tags" do + log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{foo bar}) + log.tags.should be_include("foo") + log.tags.should be_include("bar") + end + + it "should use an passed-in source" do + Puppet::Util::Log.any_instance.expects(:source=).with "foo" + Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => "foo") + end + + [:file, :line, :version].each do |attr| + it "should use #{attr} if provided" do + Puppet::Util::Log.any_instance.expects(attr.to_s + "=").with "foo" + Puppet::Util::Log.new(:level => "notice", :message => :foo, attr => "foo") + end + end + + it "should default to 'Puppet' as its source" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).source.should == "Puppet" + end + + it "should register itself with Log" do + Puppet::Util::Log.expects(:newmessage) + Puppet::Util::Log.new(:level => "notice", :message => :foo) + end + + it "should have a method for determining if a tag is present" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).should respond_to(:tagged?) + end + + it "should match a tag if any of the tags are equivalent to the passed tag as a string" do + Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{one two}).should be_tagged(:one) + end + + it "should tag itself with its log level" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).should be_tagged(:notice) + end + + it "should return its message when converted to a string" do + Puppet::Util::Log.new(:level => "notice", :message => :foo).to_s.should == "foo" + end + + it "should include its time, source, level, and message when prepared for reporting" do + log = Puppet::Util::Log.new(:level => "notice", :message => :foo) + report = log.to_report + report.should be_include("notice") + report.should be_include("foo") + report.should be_include(log.source) + report.should be_include(log.time.to_s) + end + + describe "when setting the source as a RAL object" do + it "should tag itself with any tags the source has" do + source = Puppet::Type.type(:file).new :path => "/foo/bar" + log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) + source.tags.each do |tag| + log.tags.should be_include(tag) + end + end + + it "should use the source_descriptors" do + source = stub "source" + source.stubs(:source_descriptors).returns(:tags => ["tag","tag2"], :path => "path", :version => 100) + + log = Puppet::Util::Log.new(:level => "notice", :message => :foo) + log.expects(:tag).with("tag") + log.expects(:tag).with("tag2") + log.expects(:version=).with(100) + + log.source = source + + log.source.should == "path" + end + + it "should copy over any version information" do + catalog = Puppet::Resource::Catalog.new + catalog.version = 25 + source = Puppet::Type.type(:file).new :path => "/foo/bar" + catalog.add_resource source + + log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) + log.version.should == 25 + end + + it "should copy over any file and line information" do + source = Puppet::Type.type(:file).new :path => "/foo/bar" + source.file = "/my/file" + source.line = 50 + log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) + log.file.should == "/my/file" + log.line.should == 50 + end + end + + describe "when setting the source as a non-RAL object" do + it "should not try to copy over file, version, line, or tag information" do + source = Puppet::Module.new("foo") + source.expects(:file).never + log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) + end + end + end +end diff --git a/spec/unit/util/log_spec_spec.rb b/spec/unit/util/log_spec_spec.rb deleted file mode 100755 index 7aaa580c5..000000000 --- a/spec/unit/util/log_spec_spec.rb +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/log' - -describe Puppet::Util::Log do - it "should be able to close all log destinations" do - Puppet::Util::Log.expects(:destinations).returns %w{foo bar} - Puppet::Util::Log.expects(:close).with("foo") - Puppet::Util::Log.expects(:close).with("bar") - - Puppet::Util::Log.close_all - end - - describe Puppet::Util::Log::DestConsole do - before do - @console = Puppet::Util::Log::DestConsole.new - end - - it "should colorize if Puppet[:color] is :ansi" do - Puppet[:color] = :ansi - - @console.colorize(:alert, "abc").should == "\e[0;31mabc\e[0m" - end - - it "should colorize if Puppet[:color] is 'yes'" do - Puppet[:color] = "yes" - - @console.colorize(:alert, "abc").should == "\e[0;31mabc\e[0m" - end - - it "should htmlize if Puppet[:color] is :html" do - Puppet[:color] = :html - - @console.colorize(:alert, "abc").should == "abc" - end - - it "should do nothing if Puppet[:color] is false" do - Puppet[:color] = false - - @console.colorize(:alert, "abc").should == "abc" - end - - it "should do nothing if Puppet[:color] is invalid" do - Puppet[:color] = "invalid option" - - @console.colorize(:alert, "abc").should == "abc" - end - end - - describe "instances" do - before do - Puppet::Util::Log.stubs(:newmessage) - end - - [:level, :message, :time, :remote].each do |attr| - it "should have a %s attribute" % attr do - log = Puppet::Util::Log.new :level => :notice, :message => "A test message" - log.should respond_to(attr) - log.should respond_to(attr.to_s + "=") - end - end - - it "should fail if created without a level" do - lambda { Puppet::Util::Log.new(:message => "A test message") }.should raise_error(ArgumentError) - end - - it "should fail if created without a message" do - lambda { Puppet::Util::Log.new(:level => :notice) }.should raise_error(ArgumentError) - end - - it "should make available the level passed in at initialization" do - Puppet::Util::Log.new(:level => :notice, :message => "A test message").level.should == :notice - end - - it "should make available the message passed in at initialization" do - Puppet::Util::Log.new(:level => :notice, :message => "A test message").message.should == "A test message" - end - - # LAK:NOTE I don't know why this behavior is here, I'm just testing what's in the code, - # at least at first. - it "should always convert messages to strings" do - Puppet::Util::Log.new(:level => :notice, :message => :foo).message.should == "foo" - end - - it "should convert the level to a symbol if it's passed in as a string" do - Puppet::Util::Log.new(:level => "notice", :message => :foo).level.should == :notice - end - - it "should fail if the level is not a symbol or string" do - lambda { Puppet::Util::Log.new(:level => 50, :message => :foo) }.should raise_error(ArgumentError) - end - - it "should fail if the provided level is not valid" do - Puppet::Util::Log.expects(:validlevel?).with(:notice).returns false - lambda { Puppet::Util::Log.new(:level => :notice, :message => :foo) }.should raise_error(ArgumentError) - end - - it "should set its time to the initialization time" do - time = mock 'time' - Time.expects(:now).returns time - Puppet::Util::Log.new(:level => "notice", :message => :foo).time.should equal(time) - end - - it "should make available any passed-in tags" do - log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{foo bar}) - log.tags.should be_include("foo") - log.tags.should be_include("bar") - end - - it "should use an passed-in source" do - Puppet::Util::Log.any_instance.expects(:source=).with "foo" - Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => "foo") - end - - [:file, :line, :version].each do |attr| - it "should use #{attr} if provided" do - Puppet::Util::Log.any_instance.expects(attr.to_s + "=").with "foo" - Puppet::Util::Log.new(:level => "notice", :message => :foo, attr => "foo") - end - end - - it "should default to 'Puppet' as its source" do - Puppet::Util::Log.new(:level => "notice", :message => :foo).source.should == "Puppet" - end - - it "should register itself with Log" do - Puppet::Util::Log.expects(:newmessage) - Puppet::Util::Log.new(:level => "notice", :message => :foo) - end - - it "should have a method for determining if a tag is present" do - Puppet::Util::Log.new(:level => "notice", :message => :foo).should respond_to(:tagged?) - end - - it "should match a tag if any of the tags are equivalent to the passed tag as a string" do - Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{one two}).should be_tagged(:one) - end - - it "should tag itself with its log level" do - Puppet::Util::Log.new(:level => "notice", :message => :foo).should be_tagged(:notice) - end - - it "should return its message when converted to a string" do - Puppet::Util::Log.new(:level => "notice", :message => :foo).to_s.should == "foo" - end - - it "should include its time, source, level, and message when prepared for reporting" do - log = Puppet::Util::Log.new(:level => "notice", :message => :foo) - report = log.to_report - report.should be_include("notice") - report.should be_include("foo") - report.should be_include(log.source) - report.should be_include(log.time.to_s) - end - - describe "when setting the source as a RAL object" do - it "should tag itself with any tags the source has" do - source = Puppet::Type.type(:file).new :path => "/foo/bar" - log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) - source.tags.each do |tag| - log.tags.should be_include(tag) - end - end - - it "should use the source_descriptors" do - source = stub "source" - source.stubs(:source_descriptors).returns(:tags => ["tag","tag2"], :path => "path", :version => 100) - - log = Puppet::Util::Log.new(:level => "notice", :message => :foo) - log.expects(:tag).with("tag") - log.expects(:tag).with("tag2") - log.expects(:version=).with(100) - - log.source = source - - log.source.should == "path" - end - - it "should copy over any version information" do - catalog = Puppet::Resource::Catalog.new - catalog.version = 25 - source = Puppet::Type.type(:file).new :path => "/foo/bar" - catalog.add_resource source - - log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) - log.version.should == 25 - end - - it "should copy over any file and line information" do - source = Puppet::Type.type(:file).new :path => "/foo/bar" - source.file = "/my/file" - source.line = 50 - log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) - log.file.should == "/my/file" - log.line.should == 50 - end - end - - describe "when setting the source as a non-RAL object" do - it "should not try to copy over file, version, line, or tag information" do - source = Puppet::Module.new("foo") - source.expects(:file).never - log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) - end - end - end -end diff --git a/spec/unit/util/logging_spec.rb b/spec/unit/util/logging_spec.rb new file mode 100755 index 000000000..aee308eca --- /dev/null +++ b/spec/unit/util/logging_spec.rb @@ -0,0 +1,88 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/logging' + +class LoggingTester + include Puppet::Util::Logging +end + +describe Puppet::Util::Logging do + before do + @logger = LoggingTester.new + end + + Puppet::Util::Log.eachlevel do |level| + it "should have a method for sending '#{level}' logs" do + @logger.should respond_to(level) + end + end + + it "should have a method for sending a log with a specified log level" do + @logger.expects(:to_s).returns "I'm a string!" + Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" and args[:level] == "loglevel" and args[:message] == "mymessage" } + + @logger.send_log "loglevel", "mymessage" + end + + describe "when sending a log" do + it "should use the Log's 'create' entrance method" do + Puppet::Util::Log.expects(:create) + + @logger.notice "foo" + end + + it "should send itself converted to a string as the log source" do + @logger.expects(:to_s).returns "I'm a string!" + Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" } + + @logger.notice "foo" + end + + it "should use the path of any provided resource type" do + resource = Puppet::Type.type(:mount).new :name => "foo" + + resource.expects(:path).returns "/path/to/mount".to_sym + + Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/mount" } + + resource.notice "foo" + end + + it "should use the path of any provided resource parameter" do + resource = Puppet::Type.type(:mount).new :name => "foo" + + param = resource.parameter(:name) + + param.expects(:path).returns "/path/to/param".to_sym + + Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/param" } + + param.notice "foo" + end + + it "should send the provided argument as the log message" do + Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo" } + + @logger.notice "foo" + end + + it "should join any provided arguments into a single string for the message" do + Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo bar baz" } + + @logger.notice ["foo", "bar", "baz"] + end + + [:file, :line, :version, :tags].each do |attr| + it "should include #{attr} if available" do + @logger.singleton_class.send(:attr_accessor, attr) + + @logger.send(attr.to_s + "=", "myval") + + Puppet::Util::Log.expects(:create).with { |args| args[attr] == "myval" } + @logger.notice "foo" + end + end + end +end diff --git a/spec/unit/util/logging_spec_spec.rb b/spec/unit/util/logging_spec_spec.rb deleted file mode 100755 index aee308eca..000000000 --- a/spec/unit/util/logging_spec_spec.rb +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/logging' - -class LoggingTester - include Puppet::Util::Logging -end - -describe Puppet::Util::Logging do - before do - @logger = LoggingTester.new - end - - Puppet::Util::Log.eachlevel do |level| - it "should have a method for sending '#{level}' logs" do - @logger.should respond_to(level) - end - end - - it "should have a method for sending a log with a specified log level" do - @logger.expects(:to_s).returns "I'm a string!" - Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" and args[:level] == "loglevel" and args[:message] == "mymessage" } - - @logger.send_log "loglevel", "mymessage" - end - - describe "when sending a log" do - it "should use the Log's 'create' entrance method" do - Puppet::Util::Log.expects(:create) - - @logger.notice "foo" - end - - it "should send itself converted to a string as the log source" do - @logger.expects(:to_s).returns "I'm a string!" - Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" } - - @logger.notice "foo" - end - - it "should use the path of any provided resource type" do - resource = Puppet::Type.type(:mount).new :name => "foo" - - resource.expects(:path).returns "/path/to/mount".to_sym - - Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/mount" } - - resource.notice "foo" - end - - it "should use the path of any provided resource parameter" do - resource = Puppet::Type.type(:mount).new :name => "foo" - - param = resource.parameter(:name) - - param.expects(:path).returns "/path/to/param".to_sym - - Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/param" } - - param.notice "foo" - end - - it "should send the provided argument as the log message" do - Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo" } - - @logger.notice "foo" - end - - it "should join any provided arguments into a single string for the message" do - Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo bar baz" } - - @logger.notice ["foo", "bar", "baz"] - end - - [:file, :line, :version, :tags].each do |attr| - it "should include #{attr} if available" do - @logger.singleton_class.send(:attr_accessor, attr) - - @logger.send(attr.to_s + "=", "myval") - - Puppet::Util::Log.expects(:create).with { |args| args[attr] == "myval" } - @logger.notice "foo" - end - end - end -end diff --git a/spec/unit/util/metric_spec.rb b/spec/unit/util/metric_spec.rb new file mode 100755 index 000000000..41ab4cc3e --- /dev/null +++ b/spec/unit/util/metric_spec.rb @@ -0,0 +1,95 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/metric' + +describe Puppet::Util::Metric do + before do + @metric = Puppet::Util::Metric.new("foo") + end + + it "should be aliased to Puppet::Metric" do + Puppet::Util::Metric.should equal(Puppet::Metric) + end + + [:type, :name, :value, :label, :basedir].each do |name| + it "should have a #{name} attribute" do + @metric.should respond_to(name) + @metric.should respond_to(name.to_s + "=") + end + end + + it "should default to the :rrdir as the basedir "do + Puppet.settings.expects(:value).with(:rrddir).returns "myrrd" + @metric.basedir.should == "myrrd" + end + + it "should use any provided basedir" do + @metric.basedir = "foo" + @metric.basedir.should == "foo" + end + + it "should require a name at initialization" do + lambda { Puppet::Util::Metric.new }.should raise_error(ArgumentError) + end + + it "should always convert its name to a string" do + Puppet::Util::Metric.new(:foo).name.should == "foo" + end + + it "should support a label" do + Puppet::Util::Metric.new("foo", "mylabel").label.should == "mylabel" + end + + it "should autogenerate a label if none is provided" do + Puppet::Util::Metric.new("foo_bar").label.should == "Foo bar" + end + + it "should have a method for adding values" do + @metric.should respond_to(:newvalue) + end + + it "should have a method for returning values" do + @metric.should respond_to(:values) + end + + it "should require a name and value for its values" do + lambda { @metric.newvalue }.should raise_error(ArgumentError) + end + + it "should support a label for values" do + @metric.newvalue(:foo, 10, "label") + @metric.values[0][1].should == "label" + end + + it "should autogenerate value labels if none is provided" do + @metric.newvalue("foo_bar", 10) + @metric.values[0][1].should == "Foo bar" + end + + it "should return its values sorted by label" do + @metric.newvalue(:foo, 10, "b") + @metric.newvalue(:bar, 10, "a") + + @metric.values.should == [[:bar, "a", 10], [:foo, "b", 10]] + end + + it "should use an array indexer method to retrieve individual values" do + @metric.newvalue(:foo, 10) + @metric[:foo].should == 10 + end + + it "should return nil if the named value cannot be found" do + @metric[:foo].should == 0 + end + + # LAK: I'm not taking the time to develop these tests right now. + # I expect they should actually be extracted into a separate class + # anyway. + it "should be able to graph metrics using RRDTool" + + it "should be able to create a new RRDTool database" + + it "should be able to store metrics into an RRDTool database" +end diff --git a/spec/unit/util/metric_spec_spec.rb b/spec/unit/util/metric_spec_spec.rb deleted file mode 100755 index 41ab4cc3e..000000000 --- a/spec/unit/util/metric_spec_spec.rb +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/metric' - -describe Puppet::Util::Metric do - before do - @metric = Puppet::Util::Metric.new("foo") - end - - it "should be aliased to Puppet::Metric" do - Puppet::Util::Metric.should equal(Puppet::Metric) - end - - [:type, :name, :value, :label, :basedir].each do |name| - it "should have a #{name} attribute" do - @metric.should respond_to(name) - @metric.should respond_to(name.to_s + "=") - end - end - - it "should default to the :rrdir as the basedir "do - Puppet.settings.expects(:value).with(:rrddir).returns "myrrd" - @metric.basedir.should == "myrrd" - end - - it "should use any provided basedir" do - @metric.basedir = "foo" - @metric.basedir.should == "foo" - end - - it "should require a name at initialization" do - lambda { Puppet::Util::Metric.new }.should raise_error(ArgumentError) - end - - it "should always convert its name to a string" do - Puppet::Util::Metric.new(:foo).name.should == "foo" - end - - it "should support a label" do - Puppet::Util::Metric.new("foo", "mylabel").label.should == "mylabel" - end - - it "should autogenerate a label if none is provided" do - Puppet::Util::Metric.new("foo_bar").label.should == "Foo bar" - end - - it "should have a method for adding values" do - @metric.should respond_to(:newvalue) - end - - it "should have a method for returning values" do - @metric.should respond_to(:values) - end - - it "should require a name and value for its values" do - lambda { @metric.newvalue }.should raise_error(ArgumentError) - end - - it "should support a label for values" do - @metric.newvalue(:foo, 10, "label") - @metric.values[0][1].should == "label" - end - - it "should autogenerate value labels if none is provided" do - @metric.newvalue("foo_bar", 10) - @metric.values[0][1].should == "Foo bar" - end - - it "should return its values sorted by label" do - @metric.newvalue(:foo, 10, "b") - @metric.newvalue(:bar, 10, "a") - - @metric.values.should == [[:bar, "a", 10], [:foo, "b", 10]] - end - - it "should use an array indexer method to retrieve individual values" do - @metric.newvalue(:foo, 10) - @metric[:foo].should == 10 - end - - it "should return nil if the named value cannot be found" do - @metric[:foo].should == 0 - end - - # LAK: I'm not taking the time to develop these tests right now. - # I expect they should actually be extracted into a separate class - # anyway. - it "should be able to graph metrics using RRDTool" - - it "should be able to create a new RRDTool database" - - it "should be able to store metrics into an RRDTool database" -end diff --git a/spec/unit/util/monkey_patches_spec.rb b/spec/unit/util/monkey_patches_spec.rb new file mode 100644 index 000000000..b0f61c808 --- /dev/null +++ b/spec/unit/util/monkey_patches_spec.rb @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/monkey_patches' + + diff --git a/spec/unit/util/monkey_patches_spec_spec.rb b/spec/unit/util/monkey_patches_spec_spec.rb deleted file mode 100644 index b0f61c808..000000000 --- a/spec/unit/util/monkey_patches_spec_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/monkey_patches' - - diff --git a/spec/unit/util/nagios_maker_spec.rb b/spec/unit/util/nagios_maker_spec.rb new file mode 100755 index 000000000..1e1aefcae --- /dev/null +++ b/spec/unit/util/nagios_maker_spec.rb @@ -0,0 +1,126 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-11-18. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/nagios_maker' + +describe Puppet::Util::NagiosMaker do + before do + @module = Puppet::Util::NagiosMaker + + @nagtype = stub 'nagios type', :parameters => [], :namevar => :name + Nagios::Base.stubs(:type).with(:test).returns(@nagtype) + + @provider = stub 'provider', :nagios_type => nil + @type = stub 'type', :newparam => nil, :newproperty => nil, :provide => @provider, :desc => nil, :ensurable => nil + end + + it "should be able to create a new nagios type" do + @module.should respond_to(:create_nagios_type) + end + + it "should fail if it cannot find the named Naginator type" do + Nagios::Base.stubs(:type).returns(nil) + + lambda { @module.create_nagios_type(:no_such_type) }.should raise_error(Puppet::DevError) + end + + it "should create a new RAL type with the provided name prefixed with 'nagios_'" do + Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) + @module.create_nagios_type(:test) + end + + it "should mark the created type as ensurable" do + @type.expects(:ensurable) + + Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) + @module.create_nagios_type(:test) + end + + it "should create a namevar parameter for the nagios type's name parameter" do + @type.expects(:newparam).with(:name, :namevar => true) + + Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) + @module.create_nagios_type(:test) + end + + it "should create a property for all non-namevar parameters" do + @nagtype.stubs(:parameters).returns([:one, :two]) + + @type.expects(:newproperty).with(:one) + @type.expects(:newproperty).with(:two) + @type.expects(:newproperty).with(:target) + + Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) + @module.create_nagios_type(:test) + end + + it "should skip parameters that start with integers" do + @nagtype.stubs(:parameters).returns(["2dcoords".to_sym, :other]) + + @type.expects(:newproperty).with(:other) + @type.expects(:newproperty).with(:target) + + Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) + @module.create_nagios_type(:test) + end + + it "should deduplicate the parameter list" do + @nagtype.stubs(:parameters).returns([:one, :one]) + + @type.expects(:newproperty).with(:one) + @type.expects(:newproperty).with(:target) + + Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) + @module.create_nagios_type(:test) + end + + it "should create a target property" do + @type.expects(:newproperty).with(:target) + + Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) + @module.create_nagios_type(:test) + end +end + +describe Puppet::Util::NagiosMaker, " when creating the naginator provider" do + before do + @module = Puppet::Util::NagiosMaker + @provider = stub 'provider', :nagios_type => nil + + @nagtype = stub 'nagios type', :parameters => [], :namevar => :name + Nagios::Base.stubs(:type).with(:test).returns(@nagtype) + + @type = stub 'type', :newparam => nil, :ensurable => nil, :newproperty => nil, :desc => nil + Puppet::Type.stubs(:newtype).with(:nagios_test).returns(@type) + end + + it "should add a naginator provider" do + @type.expects(:provide).with { |name, options| name == :naginator }.returns @provider + + @module.create_nagios_type(:test) + end + + it "should set Puppet::Provider::Naginator as the parent class of the provider" do + @type.expects(:provide).with { |name, options| options[:parent] == Puppet::Provider::Naginator }.returns @provider + + @module.create_nagios_type(:test) + end + + it "should use /etc/nagios/$name.cfg as the default target" do + @type.expects(:provide).with { |name, options| options[:default_target] == "/etc/nagios/nagios_test.cfg" }.returns @provider + + @module.create_nagios_type(:test) + end + + it "should trigger the lookup of the Nagios class" do + @type.expects(:provide).returns @provider + + @provider.expects(:nagios_type) + + @module.create_nagios_type(:test) + end +end diff --git a/spec/unit/util/nagios_maker_spec_spec.rb b/spec/unit/util/nagios_maker_spec_spec.rb deleted file mode 100755 index 1e1aefcae..000000000 --- a/spec/unit/util/nagios_maker_spec_spec.rb +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-11-18. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/util/nagios_maker' - -describe Puppet::Util::NagiosMaker do - before do - @module = Puppet::Util::NagiosMaker - - @nagtype = stub 'nagios type', :parameters => [], :namevar => :name - Nagios::Base.stubs(:type).with(:test).returns(@nagtype) - - @provider = stub 'provider', :nagios_type => nil - @type = stub 'type', :newparam => nil, :newproperty => nil, :provide => @provider, :desc => nil, :ensurable => nil - end - - it "should be able to create a new nagios type" do - @module.should respond_to(:create_nagios_type) - end - - it "should fail if it cannot find the named Naginator type" do - Nagios::Base.stubs(:type).returns(nil) - - lambda { @module.create_nagios_type(:no_such_type) }.should raise_error(Puppet::DevError) - end - - it "should create a new RAL type with the provided name prefixed with 'nagios_'" do - Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) - @module.create_nagios_type(:test) - end - - it "should mark the created type as ensurable" do - @type.expects(:ensurable) - - Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) - @module.create_nagios_type(:test) - end - - it "should create a namevar parameter for the nagios type's name parameter" do - @type.expects(:newparam).with(:name, :namevar => true) - - Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) - @module.create_nagios_type(:test) - end - - it "should create a property for all non-namevar parameters" do - @nagtype.stubs(:parameters).returns([:one, :two]) - - @type.expects(:newproperty).with(:one) - @type.expects(:newproperty).with(:two) - @type.expects(:newproperty).with(:target) - - Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) - @module.create_nagios_type(:test) - end - - it "should skip parameters that start with integers" do - @nagtype.stubs(:parameters).returns(["2dcoords".to_sym, :other]) - - @type.expects(:newproperty).with(:other) - @type.expects(:newproperty).with(:target) - - Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) - @module.create_nagios_type(:test) - end - - it "should deduplicate the parameter list" do - @nagtype.stubs(:parameters).returns([:one, :one]) - - @type.expects(:newproperty).with(:one) - @type.expects(:newproperty).with(:target) - - Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) - @module.create_nagios_type(:test) - end - - it "should create a target property" do - @type.expects(:newproperty).with(:target) - - Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) - @module.create_nagios_type(:test) - end -end - -describe Puppet::Util::NagiosMaker, " when creating the naginator provider" do - before do - @module = Puppet::Util::NagiosMaker - @provider = stub 'provider', :nagios_type => nil - - @nagtype = stub 'nagios type', :parameters => [], :namevar => :name - Nagios::Base.stubs(:type).with(:test).returns(@nagtype) - - @type = stub 'type', :newparam => nil, :ensurable => nil, :newproperty => nil, :desc => nil - Puppet::Type.stubs(:newtype).with(:nagios_test).returns(@type) - end - - it "should add a naginator provider" do - @type.expects(:provide).with { |name, options| name == :naginator }.returns @provider - - @module.create_nagios_type(:test) - end - - it "should set Puppet::Provider::Naginator as the parent class of the provider" do - @type.expects(:provide).with { |name, options| options[:parent] == Puppet::Provider::Naginator }.returns @provider - - @module.create_nagios_type(:test) - end - - it "should use /etc/nagios/$name.cfg as the default target" do - @type.expects(:provide).with { |name, options| options[:default_target] == "/etc/nagios/nagios_test.cfg" }.returns @provider - - @module.create_nagios_type(:test) - end - - it "should trigger the lookup of the Nagios class" do - @type.expects(:provide).returns @provider - - @provider.expects(:nagios_type) - - @module.create_nagios_type(:test) - end -end diff --git a/spec/unit/util/package_spec.rb b/spec/unit/util/package_spec.rb new file mode 100644 index 000000000..45eef4643 --- /dev/null +++ b/spec/unit/util/package_spec.rb @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/package' + +describe Puppet::Util::Package, " versioncmp" do + + it "should be able to be used as a module function" do + Puppet::Util::Package.should respond_to(:versioncmp) + end + + it "should be able to sort a long set of various unordered versions" do + ary = %w{ 1.1.6 2.3 1.1a 3.0 1.5 1 2.4 1.1-4 2.3.1 1.2 2.3.0 1.1-3 2.4b 2.4 2.40.2 2.3a.1 3.1 0002 1.1-5 1.1.a 1.06} + + newary = ary.sort { |a, b| Puppet::Util::Package.versioncmp(a,b) } + + newary.should == ["0002", "1", "1.06", "1.1-3", "1.1-4", "1.1-5", "1.1.6", "1.1.a", "1.1a", "1.2", "1.5", "2.3", "2.3.0", "2.3.1", "2.3a.1", "2.4", "2.4", "2.4b", "2.40.2", "3.0", "3.1"] + end + +end diff --git a/spec/unit/util/package_spec_spec.rb b/spec/unit/util/package_spec_spec.rb deleted file mode 100644 index 45eef4643..000000000 --- a/spec/unit/util/package_spec_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/util/package' - -describe Puppet::Util::Package, " versioncmp" do - - it "should be able to be used as a module function" do - Puppet::Util::Package.should respond_to(:versioncmp) - end - - it "should be able to sort a long set of various unordered versions" do - ary = %w{ 1.1.6 2.3 1.1a 3.0 1.5 1 2.4 1.1-4 2.3.1 1.2 2.3.0 1.1-3 2.4b 2.4 2.40.2 2.3a.1 3.1 0002 1.1-5 1.1.a 1.06} - - newary = ary.sort { |a, b| Puppet::Util::Package.versioncmp(a,b) } - - newary.should == ["0002", "1", "1.06", "1.1-3", "1.1-4", "1.1-5", "1.1.6", "1.1.a", "1.1a", "1.2", "1.5", "2.3", "2.3.0", "2.3.1", "2.3a.1", "2.4", "2.4", "2.4b", "2.40.2", "3.0", "3.1"] - end - -end diff --git a/spec/unit/util/posix_spec.rb b/spec/unit/util/posix_spec.rb new file mode 100755 index 000000000..95a5608bd --- /dev/null +++ b/spec/unit/util/posix_spec.rb @@ -0,0 +1,256 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/posix' + +class PosixTest + include Puppet::Util::POSIX +end + +describe Puppet::Util::POSIX do + before do + @posix = PosixTest.new + end + + [:group, :gr].each do |name| + it "should return :gid as the field for %s" % name do + @posix.idfield(name).should == :gid + end + + it "should return :getgrgid as the id method for %s" % name do + @posix.methodbyid(name).should == :getgrgid + end + + it "should return :getgrnam as the name method for %s" % name do + @posix.methodbyname(name).should == :getgrnam + end + end + + [:user, :pw, :passwd].each do |name| + it "should return :uid as the field for %s" % name do + @posix.idfield(name).should == :uid + end + + it "should return :getpwuid as the id method for %s" % name do + @posix.methodbyid(name).should == :getpwuid + end + + it "should return :getpwnam as the name method for %s" % name do + @posix.methodbyname(name).should == :getpwnam + end + end + + describe "when retrieving a posix field" do + before do + @thing = stub 'thing', :field => "asdf" + end + + it "should fail if no id was passed" do + lambda { @posix.get_posix_field("asdf", "bar", nil) }.should raise_error(Puppet::DevError) + end + + describe "and the id is an integer" do + it "should log an error and return nil if the specified id is greater than the maximum allowed ID" do + Puppet.settings.expects(:value).with(:maximum_uid).returns 100 + Puppet.expects(:err) + + @posix.get_posix_field("asdf", "bar", 200).should be_nil + end + + it "should use the method return by :methodbyid and return the specified field" do + Etc.expects(:getgrgid).returns @thing + + @thing.expects(:field).returns "myval" + + @posix.get_posix_field(:gr, :field, 200).should == "myval" + end + + it "should return nil if the method throws an exception" do + Etc.expects(:getgrgid).raises ArgumentError + + @thing.expects(:field).never + + @posix.get_posix_field(:gr, :field, 200).should be_nil + end + end + + describe "and the id is not an integer" do + it "should use the method return by :methodbyid and return the specified field" do + Etc.expects(:getgrnam).returns @thing + + @thing.expects(:field).returns "myval" + + @posix.get_posix_field(:gr, :field, "asdf").should == "myval" + end + + it "should return nil if the method throws an exception" do + Etc.expects(:getgrnam).raises ArgumentError + + @thing.expects(:field).never + + @posix.get_posix_field(:gr, :field, "asdf").should be_nil + end + end + end + + describe "when returning the gid" do + before do + @posix.stubs(:get_posix_field) + end + + describe "and the group is an integer" do + it "should convert integers specified as a string into an integer" do + @posix.expects(:get_posix_field).with(:group, :name, 100) + + @posix.gid("100") + end + + it "should look up the name for the group" do + @posix.expects(:get_posix_field).with(:group, :name, 100) + + @posix.gid(100) + end + + it "should return nil if the group cannot be found" do + @posix.expects(:get_posix_field).once.returns nil + @posix.expects(:search_posix_field).never + + @posix.gid(100).should be_nil + end + + it "should use the found name to look up the id" do + @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" + @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 + + @posix.gid(100).should == 100 + end + + # LAK: This is because some platforms have a broken Etc module that always return + # the same group. + it "should use :search_posix_field if the discovered id does not match the passed-in id" do + @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" + @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 50 + + @posix.expects(:search_posix_field).with(:group, :gid, 100).returns "asdf" + + @posix.gid(100).should == "asdf" + end + end + + describe "and the group is a string" do + it "should look up the gid for the group" do + @posix.expects(:get_posix_field).with(:group, :gid, "asdf") + + @posix.gid("asdf") + end + + it "should return nil if the group cannot be found" do + @posix.expects(:get_posix_field).once.returns nil + @posix.expects(:search_posix_field).never + + @posix.gid("asdf").should be_nil + end + + it "should use the found gid to look up the nam" do + @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 + @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" + + @posix.gid("asdf").should == 100 + end + + it "should use :search_posix_field if the discovered name does not match the passed-in name" do + @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 + @posix.expects(:get_posix_field).with(:group, :name, 100).returns "boo" + + @posix.expects(:search_posix_field).with(:group, :gid, "asdf").returns "asdf" + + @posix.gid("asdf").should == "asdf" + end + end + end + + describe "when returning the uid" do + before do + @posix.stubs(:get_posix_field) + end + + describe "and the group is an integer" do + it "should convert integers specified as a string into an integer" do + @posix.expects(:get_posix_field).with(:passwd, :name, 100) + + @posix.uid("100") + end + + it "should look up the name for the group" do + @posix.expects(:get_posix_field).with(:passwd, :name, 100) + + @posix.uid(100) + end + + it "should return nil if the group cannot be found" do + @posix.expects(:get_posix_field).once.returns nil + @posix.expects(:search_posix_field).never + + @posix.uid(100).should be_nil + end + + it "should use the found name to look up the id" do + @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" + @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 + + @posix.uid(100).should == 100 + end + + # LAK: This is because some platforms have a broken Etc module that always return + # the same group. + it "should use :search_posix_field if the discovered id does not match the passed-in id" do + @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" + @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 50 + + @posix.expects(:search_posix_field).with(:passwd, :uid, 100).returns "asdf" + + @posix.uid(100).should == "asdf" + end + end + + describe "and the group is a string" do + it "should look up the uid for the group" do + @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf") + + @posix.uid("asdf") + end + + it "should return nil if the group cannot be found" do + @posix.expects(:get_posix_field).once.returns nil + @posix.expects(:search_posix_field).never + + @posix.uid("asdf").should be_nil + end + + it "should use the found uid to look up the nam" do + @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 + @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" + + @posix.uid("asdf").should == 100 + end + + it "should use :search_posix_field if the discovered name does not match the passed-in name" do + @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 + @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "boo" + + @posix.expects(:search_posix_field).with(:passwd, :uid, "asdf").returns "asdf" + + @posix.uid("asdf").should == "asdf" + end + end + end + + it "should be able to iteratively search for posix values" do + @posix.should respond_to(:search_posix_field) + end + + describe "when searching for posix values iteratively" do + it "should iterate across all of the structs returned by Etc and return the appropriate field from the first matching value" + end +end diff --git a/spec/unit/util/posix_spec_spec.rb b/spec/unit/util/posix_spec_spec.rb deleted file mode 100755 index 95a5608bd..000000000 --- a/spec/unit/util/posix_spec_spec.rb +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/posix' - -class PosixTest - include Puppet::Util::POSIX -end - -describe Puppet::Util::POSIX do - before do - @posix = PosixTest.new - end - - [:group, :gr].each do |name| - it "should return :gid as the field for %s" % name do - @posix.idfield(name).should == :gid - end - - it "should return :getgrgid as the id method for %s" % name do - @posix.methodbyid(name).should == :getgrgid - end - - it "should return :getgrnam as the name method for %s" % name do - @posix.methodbyname(name).should == :getgrnam - end - end - - [:user, :pw, :passwd].each do |name| - it "should return :uid as the field for %s" % name do - @posix.idfield(name).should == :uid - end - - it "should return :getpwuid as the id method for %s" % name do - @posix.methodbyid(name).should == :getpwuid - end - - it "should return :getpwnam as the name method for %s" % name do - @posix.methodbyname(name).should == :getpwnam - end - end - - describe "when retrieving a posix field" do - before do - @thing = stub 'thing', :field => "asdf" - end - - it "should fail if no id was passed" do - lambda { @posix.get_posix_field("asdf", "bar", nil) }.should raise_error(Puppet::DevError) - end - - describe "and the id is an integer" do - it "should log an error and return nil if the specified id is greater than the maximum allowed ID" do - Puppet.settings.expects(:value).with(:maximum_uid).returns 100 - Puppet.expects(:err) - - @posix.get_posix_field("asdf", "bar", 200).should be_nil - end - - it "should use the method return by :methodbyid and return the specified field" do - Etc.expects(:getgrgid).returns @thing - - @thing.expects(:field).returns "myval" - - @posix.get_posix_field(:gr, :field, 200).should == "myval" - end - - it "should return nil if the method throws an exception" do - Etc.expects(:getgrgid).raises ArgumentError - - @thing.expects(:field).never - - @posix.get_posix_field(:gr, :field, 200).should be_nil - end - end - - describe "and the id is not an integer" do - it "should use the method return by :methodbyid and return the specified field" do - Etc.expects(:getgrnam).returns @thing - - @thing.expects(:field).returns "myval" - - @posix.get_posix_field(:gr, :field, "asdf").should == "myval" - end - - it "should return nil if the method throws an exception" do - Etc.expects(:getgrnam).raises ArgumentError - - @thing.expects(:field).never - - @posix.get_posix_field(:gr, :field, "asdf").should be_nil - end - end - end - - describe "when returning the gid" do - before do - @posix.stubs(:get_posix_field) - end - - describe "and the group is an integer" do - it "should convert integers specified as a string into an integer" do - @posix.expects(:get_posix_field).with(:group, :name, 100) - - @posix.gid("100") - end - - it "should look up the name for the group" do - @posix.expects(:get_posix_field).with(:group, :name, 100) - - @posix.gid(100) - end - - it "should return nil if the group cannot be found" do - @posix.expects(:get_posix_field).once.returns nil - @posix.expects(:search_posix_field).never - - @posix.gid(100).should be_nil - end - - it "should use the found name to look up the id" do - @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" - @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 - - @posix.gid(100).should == 100 - end - - # LAK: This is because some platforms have a broken Etc module that always return - # the same group. - it "should use :search_posix_field if the discovered id does not match the passed-in id" do - @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" - @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 50 - - @posix.expects(:search_posix_field).with(:group, :gid, 100).returns "asdf" - - @posix.gid(100).should == "asdf" - end - end - - describe "and the group is a string" do - it "should look up the gid for the group" do - @posix.expects(:get_posix_field).with(:group, :gid, "asdf") - - @posix.gid("asdf") - end - - it "should return nil if the group cannot be found" do - @posix.expects(:get_posix_field).once.returns nil - @posix.expects(:search_posix_field).never - - @posix.gid("asdf").should be_nil - end - - it "should use the found gid to look up the nam" do - @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 - @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" - - @posix.gid("asdf").should == 100 - end - - it "should use :search_posix_field if the discovered name does not match the passed-in name" do - @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 - @posix.expects(:get_posix_field).with(:group, :name, 100).returns "boo" - - @posix.expects(:search_posix_field).with(:group, :gid, "asdf").returns "asdf" - - @posix.gid("asdf").should == "asdf" - end - end - end - - describe "when returning the uid" do - before do - @posix.stubs(:get_posix_field) - end - - describe "and the group is an integer" do - it "should convert integers specified as a string into an integer" do - @posix.expects(:get_posix_field).with(:passwd, :name, 100) - - @posix.uid("100") - end - - it "should look up the name for the group" do - @posix.expects(:get_posix_field).with(:passwd, :name, 100) - - @posix.uid(100) - end - - it "should return nil if the group cannot be found" do - @posix.expects(:get_posix_field).once.returns nil - @posix.expects(:search_posix_field).never - - @posix.uid(100).should be_nil - end - - it "should use the found name to look up the id" do - @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" - @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 - - @posix.uid(100).should == 100 - end - - # LAK: This is because some platforms have a broken Etc module that always return - # the same group. - it "should use :search_posix_field if the discovered id does not match the passed-in id" do - @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" - @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 50 - - @posix.expects(:search_posix_field).with(:passwd, :uid, 100).returns "asdf" - - @posix.uid(100).should == "asdf" - end - end - - describe "and the group is a string" do - it "should look up the uid for the group" do - @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf") - - @posix.uid("asdf") - end - - it "should return nil if the group cannot be found" do - @posix.expects(:get_posix_field).once.returns nil - @posix.expects(:search_posix_field).never - - @posix.uid("asdf").should be_nil - end - - it "should use the found uid to look up the nam" do - @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 - @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" - - @posix.uid("asdf").should == 100 - end - - it "should use :search_posix_field if the discovered name does not match the passed-in name" do - @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 - @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "boo" - - @posix.expects(:search_posix_field).with(:passwd, :uid, "asdf").returns "asdf" - - @posix.uid("asdf").should == "asdf" - end - end - end - - it "should be able to iteratively search for posix values" do - @posix.should respond_to(:search_posix_field) - end - - describe "when searching for posix values iteratively" do - it "should iterate across all of the structs returned by Etc and return the appropriate field from the first matching value" - end -end diff --git a/spec/unit/util/queue/stomp_spec.rb b/spec/unit/util/queue/stomp_spec.rb new file mode 100755 index 000000000..fec179018 --- /dev/null +++ b/spec/unit/util/queue/stomp_spec.rb @@ -0,0 +1,140 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/util/queue' + +describe Puppet::Util::Queue do + confine "Missing Stomp" => Puppet.features.stomp? + + it 'should load :stomp client appropriately' do + Puppet.settings.stubs(:value).returns 'faux_queue_source' + Puppet::Util::Queue.queue_type_to_class(:stomp).name.should == 'Puppet::Util::Queue::Stomp' + end +end + +describe 'Puppet::Util::Queue::Stomp' do + confine "Missing Stomp" => Puppet.features.stomp? + + before do + # So we make sure we never create a real client instance. + # Otherwise we'll try to connect, and that's bad. + Stomp::Client.stubs(:new).returns stub("client") + end + + it 'should be registered with Puppet::Util::Queue as :stomp type' do + Puppet::Util::Queue.queue_type_to_class(:stomp).should == Puppet::Util::Queue::Stomp + end + + describe "when initializing" do + it "should create a Stomp client instance" do + Stomp::Client.expects(:new).returns stub("stomp_client") + Puppet::Util::Queue::Stomp.new + end + + it "should provide helpful failures when the queue source is not a valid source" do + # Stub rather than expect, so we can include the source in the error + Puppet.settings.stubs(:value).with(:queue_source).returns "-----" + + lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) + end + + it "should fail unless the queue source is a stomp URL" do + # Stub rather than expect, so we can include the source in the error + Puppet.settings.stubs(:value).with(:queue_source).returns "http://foo/bar" + + lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) + end + + it "should fail somewhat helpfully if the Stomp client cannot be created" do + Stomp::Client.expects(:new).raises RuntimeError + lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) + end + + list = %w{user password host port} + {"user" => "myuser", "password" => "mypass", "host" => "foohost", "port" => 42}.each do |name, value| + it "should use the #{name} from the queue source as the queueing #{name}" do + Puppet.settings.expects(:value).with(:queue_source).returns "stomp://myuser:mypass@foohost:42/" + + Stomp::Client.expects(:new).with { |*args| args[list.index(name)] == value } + Puppet::Util::Queue::Stomp.new + end + end + + it "should create a reliable client instance" do + Puppet.settings.expects(:value).with(:queue_source).returns "stomp://myuser@foohost:42/" + + Stomp::Client.expects(:new).with { |*args| args[4] == true } + Puppet::Util::Queue::Stomp.new + end + end + + describe "when sending a message" do + before do + @client = stub 'client' + Stomp::Client.stubs(:new).returns @client + @queue = Puppet::Util::Queue::Stomp.new + end + + it "should send it to the queue client instance" do + @client.expects(:send).with { |queue, msg, options| msg == "Smite!" } + @queue.send_message('fooqueue', 'Smite!') + end + + it "should send it to the transformed queue name" do + @client.expects(:send).with { |queue, msg, options| queue == "/queue/fooqueue" } + @queue.send_message('fooqueue', 'Smite!') + end + + it "should send it as a persistent message" do + @client.expects(:send).with { |queue, msg, options| options[:persistent] == true } + @queue.send_message('fooqueue', 'Smite!') + end + end + + describe "when subscribing to a queue" do + before do + @client = stub 'client', :acknowledge => true + Stomp::Client.stubs(:new).returns @client + @queue = Puppet::Util::Queue::Stomp.new + end + + it "should subscribe via the queue client instance" do + @client.expects(:subscribe) + @queue.subscribe('fooqueue') + end + + it "should subscribe to the transformed queue name" do + @client.expects(:subscribe).with { |queue, options| queue == "/queue/fooqueue" } + @queue.subscribe('fooqueue') + end + + it "should specify that its messages should be acknowledged" do + @client.expects(:subscribe).with { |queue, options| options[:ack] == :client } + @queue.subscribe('fooqueue') + end + + it "should yield the body of any received message" do + message = mock 'message' + message.expects(:body).returns "mybody" + + @client.expects(:subscribe).yields(message) + + body = nil + @queue.subscribe('fooqueue') { |b| body = b } + body.should == "mybody" + end + + it "should acknowledge all successfully processed messages" do + message = stub 'message', :body => "mybode" + + @client.stubs(:subscribe).yields(message) + @client.expects(:acknowledge).with(message) + + @queue.subscribe('fooqueue') { |b| "eh" } + end + end + + it 'should transform the simple queue name to "/queue/"' do + Puppet::Util::Queue::Stomp.new.stompify_target('blah').should == '/queue/blah' + end +end diff --git a/spec/unit/util/queue/stomp_spec_spec.rb b/spec/unit/util/queue/stomp_spec_spec.rb deleted file mode 100755 index fec179018..000000000 --- a/spec/unit/util/queue/stomp_spec_spec.rb +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/util/queue' - -describe Puppet::Util::Queue do - confine "Missing Stomp" => Puppet.features.stomp? - - it 'should load :stomp client appropriately' do - Puppet.settings.stubs(:value).returns 'faux_queue_source' - Puppet::Util::Queue.queue_type_to_class(:stomp).name.should == 'Puppet::Util::Queue::Stomp' - end -end - -describe 'Puppet::Util::Queue::Stomp' do - confine "Missing Stomp" => Puppet.features.stomp? - - before do - # So we make sure we never create a real client instance. - # Otherwise we'll try to connect, and that's bad. - Stomp::Client.stubs(:new).returns stub("client") - end - - it 'should be registered with Puppet::Util::Queue as :stomp type' do - Puppet::Util::Queue.queue_type_to_class(:stomp).should == Puppet::Util::Queue::Stomp - end - - describe "when initializing" do - it "should create a Stomp client instance" do - Stomp::Client.expects(:new).returns stub("stomp_client") - Puppet::Util::Queue::Stomp.new - end - - it "should provide helpful failures when the queue source is not a valid source" do - # Stub rather than expect, so we can include the source in the error - Puppet.settings.stubs(:value).with(:queue_source).returns "-----" - - lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) - end - - it "should fail unless the queue source is a stomp URL" do - # Stub rather than expect, so we can include the source in the error - Puppet.settings.stubs(:value).with(:queue_source).returns "http://foo/bar" - - lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) - end - - it "should fail somewhat helpfully if the Stomp client cannot be created" do - Stomp::Client.expects(:new).raises RuntimeError - lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) - end - - list = %w{user password host port} - {"user" => "myuser", "password" => "mypass", "host" => "foohost", "port" => 42}.each do |name, value| - it "should use the #{name} from the queue source as the queueing #{name}" do - Puppet.settings.expects(:value).with(:queue_source).returns "stomp://myuser:mypass@foohost:42/" - - Stomp::Client.expects(:new).with { |*args| args[list.index(name)] == value } - Puppet::Util::Queue::Stomp.new - end - end - - it "should create a reliable client instance" do - Puppet.settings.expects(:value).with(:queue_source).returns "stomp://myuser@foohost:42/" - - Stomp::Client.expects(:new).with { |*args| args[4] == true } - Puppet::Util::Queue::Stomp.new - end - end - - describe "when sending a message" do - before do - @client = stub 'client' - Stomp::Client.stubs(:new).returns @client - @queue = Puppet::Util::Queue::Stomp.new - end - - it "should send it to the queue client instance" do - @client.expects(:send).with { |queue, msg, options| msg == "Smite!" } - @queue.send_message('fooqueue', 'Smite!') - end - - it "should send it to the transformed queue name" do - @client.expects(:send).with { |queue, msg, options| queue == "/queue/fooqueue" } - @queue.send_message('fooqueue', 'Smite!') - end - - it "should send it as a persistent message" do - @client.expects(:send).with { |queue, msg, options| options[:persistent] == true } - @queue.send_message('fooqueue', 'Smite!') - end - end - - describe "when subscribing to a queue" do - before do - @client = stub 'client', :acknowledge => true - Stomp::Client.stubs(:new).returns @client - @queue = Puppet::Util::Queue::Stomp.new - end - - it "should subscribe via the queue client instance" do - @client.expects(:subscribe) - @queue.subscribe('fooqueue') - end - - it "should subscribe to the transformed queue name" do - @client.expects(:subscribe).with { |queue, options| queue == "/queue/fooqueue" } - @queue.subscribe('fooqueue') - end - - it "should specify that its messages should be acknowledged" do - @client.expects(:subscribe).with { |queue, options| options[:ack] == :client } - @queue.subscribe('fooqueue') - end - - it "should yield the body of any received message" do - message = mock 'message' - message.expects(:body).returns "mybody" - - @client.expects(:subscribe).yields(message) - - body = nil - @queue.subscribe('fooqueue') { |b| body = b } - body.should == "mybody" - end - - it "should acknowledge all successfully processed messages" do - message = stub 'message', :body => "mybode" - - @client.stubs(:subscribe).yields(message) - @client.expects(:acknowledge).with(message) - - @queue.subscribe('fooqueue') { |b| "eh" } - end - end - - it 'should transform the simple queue name to "/queue/"' do - Puppet::Util::Queue::Stomp.new.stompify_target('blah').should == '/queue/blah' - end -end diff --git a/spec/unit/util/queue_spec.rb b/spec/unit/util/queue_spec.rb new file mode 100755 index 000000000..c8a75550e --- /dev/null +++ b/spec/unit/util/queue_spec.rb @@ -0,0 +1,96 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/util/queue' +require 'spec/mocks' + +def make_test_client_class(n) + c = Class.new do + class < make_test_client_class('Bogus::Default'), :setup => make_test_client_class('Bogus::Setup') } + +describe Puppet::Util::Queue do + before :all do + mod.register_queue_type(client_classes[:default], :default) + mod.register_queue_type(client_classes[:setup], :setup) + end + + before :each do + @class = Class.new do + extend mod + end + end + + after :all do + instances = mod.instance_hash(:queue_clients) + [:default, :setup, :bogus, :aardvark, :conflict, :test_a, :test_b].each{ |x| instances.delete(x) } + end + + context 'when determining a type name from a class' do + it 'should handle a simple one-word class name' do + mod.queue_type_from_class(make_test_client_class('Foo')).should == :foo + end + + it 'should handle a simple two-word class name' do + mod.queue_type_from_class(make_test_client_class('FooBar')).should == :foo_bar + end + + it 'should handle a two-part class name with one terminating word' do + mod.queue_type_from_class(make_test_client_class('Foo::Bar')).should == :bar + end + + it 'should handle a two-part class name with two terminating words' do + mod.queue_type_from_class(make_test_client_class('Foo::BarBah')).should == :bar_bah + end + end + + context 'when registering a queue client class' do + c = make_test_client_class('Foo::Bogus') + it 'uses the proper default name logic when type is unspecified' do + mod.register_queue_type(c) + mod.queue_type_to_class(:bogus).should == c + end + + it 'uses an explicit type name when provided' do + mod.register_queue_type(c, :aardvark) + mod.queue_type_to_class(:aardvark).should == c + end + + it 'throws an exception when type names conflict' do + mod.register_queue_type( make_test_client_class('Conflict') ) + lambda { mod.register_queue_type( c, :conflict) }.should raise_error + end + + it 'handle multiple, non-conflicting registrations' do + a = make_test_client_class('TestA') + b = make_test_client_class('TestB') + mod.register_queue_type(a) + mod.register_queue_type(b) + mod.queue_type_to_class(:test_a).should == a + mod.queue_type_to_class(:test_b).should == b + end + + it 'throws an exception when type name is unknown' do + lambda { mod.queue_type_to_class(:nope) }.should raise_error + end + end + + context 'when determining client type' do + it 'returns client class based on the :queue_type setting' do + Puppet.settings.expects(:value).with(:queue_type).returns(:myqueue) + Puppet::Util::Queue.expects(:queue_type_to_class).with(:myqueue).returns "eh" + @class.client_class.should == "eh" + end + end +end diff --git a/spec/unit/util/queue_spec_spec.rb b/spec/unit/util/queue_spec_spec.rb deleted file mode 100755 index c8a75550e..000000000 --- a/spec/unit/util/queue_spec_spec.rb +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/util/queue' -require 'spec/mocks' - -def make_test_client_class(n) - c = Class.new do - class < make_test_client_class('Bogus::Default'), :setup => make_test_client_class('Bogus::Setup') } - -describe Puppet::Util::Queue do - before :all do - mod.register_queue_type(client_classes[:default], :default) - mod.register_queue_type(client_classes[:setup], :setup) - end - - before :each do - @class = Class.new do - extend mod - end - end - - after :all do - instances = mod.instance_hash(:queue_clients) - [:default, :setup, :bogus, :aardvark, :conflict, :test_a, :test_b].each{ |x| instances.delete(x) } - end - - context 'when determining a type name from a class' do - it 'should handle a simple one-word class name' do - mod.queue_type_from_class(make_test_client_class('Foo')).should == :foo - end - - it 'should handle a simple two-word class name' do - mod.queue_type_from_class(make_test_client_class('FooBar')).should == :foo_bar - end - - it 'should handle a two-part class name with one terminating word' do - mod.queue_type_from_class(make_test_client_class('Foo::Bar')).should == :bar - end - - it 'should handle a two-part class name with two terminating words' do - mod.queue_type_from_class(make_test_client_class('Foo::BarBah')).should == :bar_bah - end - end - - context 'when registering a queue client class' do - c = make_test_client_class('Foo::Bogus') - it 'uses the proper default name logic when type is unspecified' do - mod.register_queue_type(c) - mod.queue_type_to_class(:bogus).should == c - end - - it 'uses an explicit type name when provided' do - mod.register_queue_type(c, :aardvark) - mod.queue_type_to_class(:aardvark).should == c - end - - it 'throws an exception when type names conflict' do - mod.register_queue_type( make_test_client_class('Conflict') ) - lambda { mod.register_queue_type( c, :conflict) }.should raise_error - end - - it 'handle multiple, non-conflicting registrations' do - a = make_test_client_class('TestA') - b = make_test_client_class('TestB') - mod.register_queue_type(a) - mod.register_queue_type(b) - mod.queue_type_to_class(:test_a).should == a - mod.queue_type_to_class(:test_b).should == b - end - - it 'throws an exception when type name is unknown' do - lambda { mod.queue_type_to_class(:nope) }.should raise_error - end - end - - context 'when determining client type' do - it 'returns client class based on the :queue_type setting' do - Puppet.settings.expects(:value).with(:queue_type).returns(:myqueue) - Puppet::Util::Queue.expects(:queue_type_to_class).with(:myqueue).returns "eh" - @class.client_class.should == "eh" - end - end -end diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb new file mode 100755 index 000000000..7113b9539 --- /dev/null +++ b/spec/unit/util/rdoc/parser_spec.rb @@ -0,0 +1,540 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/resource/type_collection' +require 'puppet/util/rdoc/parser' +require 'puppet/util/rdoc/code_objects' +require 'rdoc/options' +require 'rdoc/rdoc' + +describe RDoc::Parser do + before :each do + File.stubs(:stat).with("init.pp") + @top_level = stub_everything 'toplevel', :file_relative_name => "init.pp" + @parser = RDoc::Parser.new(@top_level, "module/manifests/init.pp", nil, Options.instance, RDoc::Stats.new) + end + + describe "when scanning files" do + it "should parse puppet files with the puppet parser" do + @parser.stubs(:scan_top_level) + parser = stub 'parser' + Puppet::Parser::Parser.expects(:new).returns(parser) + parser.expects(:parse) + parser.expects(:file=).with("module/manifests/init.pp") + + @parser.scan + end + + it "should scan the ast for Puppet files" do + parser = stub_everything 'parser' + Puppet::Parser::Parser.stubs(:new).returns(parser) + + @parser.expects(:scan_top_level) + + @parser.scan + end + + it "should return a PuppetTopLevel to RDoc" do + parser = stub_everything 'parser' + Puppet::Parser::Parser.stubs(:new).returns(parser) + + @parser.expects(:scan_top_level) + + @parser.scan.should be_a(RDoc::PuppetTopLevel) + end + end + + describe "when scanning top level entities" do + before :each do + @resource_type_collection = stub_everything 'resource_type_collection' + @parser.ast = @resource_type_collection + @parser.stubs(:split_module).returns("module") + + @topcontainer = stub_everything 'topcontainer' + @container = stub_everything 'container' + @module = stub_everything 'module' + @container.stubs(:add_module).returns(@module) + @parser.stubs(:get_class_or_module).returns([@container, "module"]) + end + + it "should read any present README as module documentation" do + FileTest.stubs(:readable?).returns(true) + File.stubs(:open).returns("readme") + @parser.stubs(:parse_elements) + + @module.expects(:comment=).with("readme") + + @parser.scan_top_level(@topcontainer) + end + + it "should tell the container its module name" do + @parser.stubs(:parse_elements) + + @topcontainer.expects(:module_name=).with("module") + + @parser.scan_top_level(@topcontainer) + end + + it "should not document our toplevel if it isn't a valid module" do + @parser.stubs(:split_module).returns(nil) + + @topcontainer.expects(:document_self=).with(false) + @parser.expects(:parse_elements).never + + @parser.scan_top_level(@topcontainer) + end + + it "should set the module as global if we parse the global manifests (ie module)" do + @parser.stubs(:split_module).returns("") + @parser.stubs(:parse_elements) + + @topcontainer.expects(:global=).with(true) + + @parser.scan_top_level(@topcontainer) + end + + it "should attach this module container to the toplevel container" do + @parser.stubs(:parse_elements) + + @container.expects(:add_module).with(RDoc::PuppetModule, "module").returns(@module) + + @parser.scan_top_level(@topcontainer) + end + + it "should defer ast parsing to parse_elements for this module" do + @parser.expects(:parse_elements).with(@module) + + @parser.scan_top_level(@topcontainer) + end + + it "should defer plugins parsing to parse_plugins for this module" do + @parser.input_file_name = "module/plugins/puppet/parser/function.rb" + + @parser.expects(:parse_plugins).with(@module) + + @parser.scan_top_level(@topcontainer) + end + end + + describe "when finding modules from filepath" do + before :each do + Puppet::Module.stubs(:modulepath).returns("/path/to/modules") + end + + it "should return the module name for modulized puppet manifests" do + File.stubs(:expand_path).returns("/path/to/module/manifests/init.pp") + File.stubs(:identical?).with("/path/to", "/path/to/modules").returns(true) + @parser.split_module("/path/to/modules/mymodule/manifests/init.pp").should == "module" + end + + it "should return for manifests not under module path" do + File.stubs(:expand_path).returns("/path/to/manifests/init.pp") + File.stubs(:identical?).returns(false) + @parser.split_module("/path/to/manifests/init.pp").should == "" + end + end + + describe "when parsing AST elements" do + before :each do + @klass = stub_everything 'klass', :file => "module/manifests/init.pp", :name => "myclass", :type => :hostclass + @definition = stub_everything 'definition', :file => "module/manifests/init.pp", :type => :definition, :name => "mydef" + @node = stub_everything 'node', :file => "module/manifests/init.pp", :type => :node, :name => "mynode" + + @resource_type_collection = Puppet::Resource::TypeCollection.new("env") + @parser.ast = @resource_type_collection + + @container = stub_everything 'container' + end + + it "should document classes in the parsed file" do + @resource_type_collection.add_hostclass(@klass) + + @parser.expects(:document_class).with("myclass", @klass, @container) + + @parser.parse_elements(@container) + end + + it "should not document class parsed in an other file" do + @klass.stubs(:file).returns("/not/same/path/file.pp") + @resource_type_collection.add_hostclass(@klass) + + @parser.expects(:document_class).with("myclass", @klass, @container).never + + @parser.parse_elements(@container) + end + + it "should document vardefs for the main class" do + @klass.stubs(:name).returns :main + @resource_type_collection.add_hostclass(@klass) + + code = stub 'code', :is_a? => false + @klass.stubs(:name).returns("") + @klass.stubs(:code).returns(code) + + @parser.expects(:scan_for_vardef).with(@container, code) + + @parser.parse_elements(@container) + end + + it "should document definitions in the parsed file" do + @resource_type_collection.add_definition(@definition) + + @parser.expects(:document_define).with("mydef", @definition, @container) + + @parser.parse_elements(@container) + end + + it "should not document definitions parsed in an other file" do + @definition.stubs(:file).returns("/not/same/path/file.pp") + @resource_type_collection.add_definition(@definition) + + @parser.expects(:document_define).with("mydef", @definition, @container).never + + @parser.parse_elements(@container) + end + + it "should document nodes in the parsed file" do + @resource_type_collection.add_node(@node) + + @parser.expects(:document_node).with("mynode", @node, @container) + + @parser.parse_elements(@container) + end + + it "should not document node parsed in an other file" do + @node.stubs(:file).returns("/not/same/path/file.pp") + @resource_type_collection.add_node(@node) + + @parser.expects(:document_node).with("mynode", @node, @container).never + + @parser.parse_elements(@container) + end + end + + describe "when documenting definition" do + before(:each) do + @define = stub_everything 'define', :arguments => [], :doc => "mydoc", :file => "file", :line => 42 + @class = stub_everything 'class' + @parser.stubs(:get_class_or_module).returns([@class, "mydef"]) + end + + it "should register a RDoc method to the current container" do + @class.expects(:add_method).with { |m| m.name == "mydef"} + @parser.document_define("mydef", @define, @class) + end + + it "should attach the documentation to this method" do + @class.expects(:add_method).with { |m| m.comment = "mydoc" } + + @parser.document_define("mydef", @define, @class) + end + + it "should produce a better error message on unhandled exception" do + @class.expects(:add_method).raises(ArgumentError) + + lambda { @parser.document_define("mydef", @define, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) + end + + it "should convert all definition parameter to string" do + arg = stub 'arg' + val = stub 'val' + + @define.stubs(:arguments).returns({arg => val}) + + arg.expects(:to_s).returns("arg") + val.expects(:to_s).returns("val") + + @parser.document_define("mydef", @define, @class) + end + end + + describe "when documenting nodes" do + before :each do + @code = stub_everything 'code' + @node = stub_everything 'node', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 + @rdoc_node = stub_everything 'rdocnode' + + @class = stub_everything 'class' + @class.stubs(:add_node).returns(@rdoc_node) + end + + it "should add a node to the current container" do + @class.expects(:add_node).with("mynode", "parent").returns(@rdoc_node) + @parser.document_node("mynode", @node, @class) + end + + it "should associate the node documentation to the rdoc node" do + @rdoc_node.expects(:comment=).with("mydoc") + @parser.document_node("mynode", @node, @class) + end + + it "should scan for include and require" do + @parser.expects(:scan_for_include_or_require).with(@rdoc_node, @code) + @parser.document_node("mynode", @node, @class) + end + + it "should scan for variable definition" do + @parser.expects(:scan_for_vardef).with(@rdoc_node, @code) + @parser.document_node("mynode", @node, @class) + end + + it "should scan for resources if needed" do + Puppet.settings.stubs(:[]).with(:document_all).returns(true) + @parser.expects(:scan_for_resource).with(@rdoc_node, @code) + @parser.document_node("mynode", @node, @class) + end + + it "should produce a better error message on unhandled exception" do + @class.stubs(:add_node).raises(ArgumentError) + + lambda { @parser.document_node("mynode", @node, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) + end + end + + describe "when documenting classes" do + before :each do + @code = stub_everything 'code' + @class = stub_everything 'class', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 + @rdoc_class = stub_everything 'rdoc-class' + + @module = stub_everything 'class' + @module.stubs(:add_class).returns(@rdoc_class) + @parser.stubs(:get_class_or_module).returns([@module, "myclass"]) + end + + it "should add a class to the current container" do + @module.expects(:add_class).with(RDoc::PuppetClass, "myclass", "parent").returns(@rdoc_class) + @parser.document_class("mynode", @class, @module) + end + + it "should set the superclass" do + @rdoc_class.expects(:superclass=).with("parent") + @parser.document_class("mynode", @class, @module) + end + + it "should associate the node documentation to the rdoc class" do + @rdoc_class.expects(:comment=).with("mydoc") + @parser.document_class("mynode", @class, @module) + end + + it "should scan for include and require" do + @parser.expects(:scan_for_include_or_require).with(@rdoc_class, @code) + @parser.document_class("mynode", @class, @module) + end + + it "should scan for resources if needed" do + Puppet.settings.stubs(:[]).with(:document_all).returns(true) + @parser.expects(:scan_for_resource).with(@rdoc_class, @code) + @parser.document_class("mynode", @class, @module) + end + + it "should produce a better error message on unhandled exception" do + @module.stubs(:add_class).raises(ArgumentError) + + lambda { @parser.document_class("mynode", @class, @module) }.should raise_error(Puppet::ParseError, /in file at line 42/) + end + end + + describe "when scanning for includes and requires" do + + def create_stmt(name) + stmt_value = stub "#{name}_value", :value => "myclass" + stmt = stub_everything 'stmt', :name => name, :arguments => [stmt_value], :doc => "mydoc" + stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) + stmt.stubs(:is_a?).with(Puppet::Parser::AST::Function).returns(true) + stmt + end + + before(:each) do + @class = stub_everything 'class' + @code = stub_everything 'code' + @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) + end + + it "should also scan mono-instruction code" do + @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } + + @parser.scan_for_include_or_require(@class,create_stmt("include")) + end + + it "should register recursively includes to the current container" do + @code.stubs(:children).returns([ create_stmt("include") ]) + + @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } + @parser.scan_for_include_or_require(@class, [@code]) + end + + it "should register requires to the current container" do + @code.stubs(:children).returns([ create_stmt("require") ]) + + @class.expects(:add_require).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } + @parser.scan_for_include_or_require(@class, [@code]) + end + end + + describe "when scanning for realized virtual resources" do + + def create_stmt + stmt_value = stub "resource_ref", :to_s => "File[\"/tmp/a\"]" + stmt = stub_everything 'stmt', :name => "realize", :arguments => [stmt_value], :doc => "mydoc" + stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) + stmt.stubs(:is_a?).with(Puppet::Parser::AST::Function).returns(true) + stmt + end + + before(:each) do + @class = stub_everything 'class' + @code = stub_everything 'code' + @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) + end + + it "should also scan mono-instruction code" do + @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } + + @parser.scan_for_realize(@class,create_stmt()) + end + + it "should register recursively includes to the current container" do + @code.stubs(:children).returns([ create_stmt() ]) + + @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } + @parser.scan_for_realize(@class, [@code]) + end + end + + describe "when scanning for variable definition" do + before :each do + @class = stub_everything 'class' + + @stmt = stub_everything 'stmt', :name => "myvar", :value => "myvalue", :doc => "mydoc" + @stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) + @stmt.stubs(:is_a?).with(Puppet::Parser::AST::VarDef).returns(true) + + @code = stub_everything 'code' + @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) + end + + it "should recursively register variables to the current container" do + @code.stubs(:children).returns([ @stmt ]) + + @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } + @parser.scan_for_vardef(@class, [ @code ]) + end + + it "should also scan mono-instruction code" do + @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } + + @parser.scan_for_vardef(@class, @stmt) + end + end + + describe "when scanning for resources" do + before :each do + @class = stub_everything 'class' + + param = stub 'params', :children => [] + @stmt = stub_everything 'stmt', :type => "File", :title => "myfile", :doc => "mydoc", :params => param + @stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) + @stmt.stubs(:is_a?).with(Puppet::Parser::AST::Resource).returns(true) + + @code = stub_everything 'code' + @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) + end + + it "should register a PuppetResource to the current container" do + @code.stubs(:children).returns([ @stmt ]) + + @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } + @parser.scan_for_resource(@class, [ @code ]) + end + + it "should also scan mono-instruction code" do + @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } + + @parser.scan_for_resource(@class, @stmt) + end + end + + describe "when parsing plugins" do + before :each do + @container = stub 'container' + end + + it "should delegate parsing custom facts to parse_facts" do + @parser = RDoc::Parser.new(@top_level, "module/manifests/plugins/puppet/facter/test.rb", nil, Options.instance, RDoc::Stats.new) + + @parser.expects(:parse_fact).with(@container) + @parser.parse_plugins(@container) + end + + it "should delegate parsing plugins to parse_plugins" do + @parser = RDoc::Parser.new(@top_level, "module/manifests/plugins/puppet/functions/test.rb", nil, Options.instance, RDoc::Stats.new) + + @parser.expects(:parse_puppet_plugin).with(@container) + @parser.parse_plugins(@container) + end + end + + describe "when parsing plugins" do + before :each do + @container = stub_everything 'container' + end + + it "should add custom functions to the container" do + File.stubs(:open).yields("# documentation + module Puppet::Parser::Functions + newfunction(:myfunc, :type => :rvalue) do |args| + File.dirname(args[0]) + end + end".split("\n")) + + @container.expects(:add_plugin).with do |plugin| + plugin.comment == "documentation\n" #and + plugin.name == "myfunc" + end + + @parser.parse_puppet_plugin(@container) + end + + it "should add custom types to the container" do + File.stubs(:open).yields("# documentation + Puppet::Type.newtype(:mytype) do + end".split("\n")) + + @container.expects(:add_plugin).with do |plugin| + plugin.comment == "documentation\n" #and + plugin.name == "mytype" + end + + @parser.parse_puppet_plugin(@container) + end + end + + describe "when parsing facts" do + before :each do + @container = stub_everything 'container' + File.stubs(:open).yields(["# documentation", "Facter.add('myfact') do", "confine :kernel => :linux", "end"]) + end + + it "should add facts to the container" do + @container.expects(:add_fact).with do |fact| + fact.comment == "documentation\n" and + fact.name == "myfact" + end + + @parser.parse_fact(@container) + end + + it "should add confine to the parsed facts" do + ourfact = nil + @container.expects(:add_fact).with do |fact| + ourfact = fact + true + end + + @parser.parse_fact(@container) + ourfact.confine.should == { :type => "kernel", :value => ":linux" } + end + end +end diff --git a/spec/unit/util/rdoc/parser_spec_spec.rb b/spec/unit/util/rdoc/parser_spec_spec.rb deleted file mode 100755 index 7113b9539..000000000 --- a/spec/unit/util/rdoc/parser_spec_spec.rb +++ /dev/null @@ -1,540 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/resource/type_collection' -require 'puppet/util/rdoc/parser' -require 'puppet/util/rdoc/code_objects' -require 'rdoc/options' -require 'rdoc/rdoc' - -describe RDoc::Parser do - before :each do - File.stubs(:stat).with("init.pp") - @top_level = stub_everything 'toplevel', :file_relative_name => "init.pp" - @parser = RDoc::Parser.new(@top_level, "module/manifests/init.pp", nil, Options.instance, RDoc::Stats.new) - end - - describe "when scanning files" do - it "should parse puppet files with the puppet parser" do - @parser.stubs(:scan_top_level) - parser = stub 'parser' - Puppet::Parser::Parser.expects(:new).returns(parser) - parser.expects(:parse) - parser.expects(:file=).with("module/manifests/init.pp") - - @parser.scan - end - - it "should scan the ast for Puppet files" do - parser = stub_everything 'parser' - Puppet::Parser::Parser.stubs(:new).returns(parser) - - @parser.expects(:scan_top_level) - - @parser.scan - end - - it "should return a PuppetTopLevel to RDoc" do - parser = stub_everything 'parser' - Puppet::Parser::Parser.stubs(:new).returns(parser) - - @parser.expects(:scan_top_level) - - @parser.scan.should be_a(RDoc::PuppetTopLevel) - end - end - - describe "when scanning top level entities" do - before :each do - @resource_type_collection = stub_everything 'resource_type_collection' - @parser.ast = @resource_type_collection - @parser.stubs(:split_module).returns("module") - - @topcontainer = stub_everything 'topcontainer' - @container = stub_everything 'container' - @module = stub_everything 'module' - @container.stubs(:add_module).returns(@module) - @parser.stubs(:get_class_or_module).returns([@container, "module"]) - end - - it "should read any present README as module documentation" do - FileTest.stubs(:readable?).returns(true) - File.stubs(:open).returns("readme") - @parser.stubs(:parse_elements) - - @module.expects(:comment=).with("readme") - - @parser.scan_top_level(@topcontainer) - end - - it "should tell the container its module name" do - @parser.stubs(:parse_elements) - - @topcontainer.expects(:module_name=).with("module") - - @parser.scan_top_level(@topcontainer) - end - - it "should not document our toplevel if it isn't a valid module" do - @parser.stubs(:split_module).returns(nil) - - @topcontainer.expects(:document_self=).with(false) - @parser.expects(:parse_elements).never - - @parser.scan_top_level(@topcontainer) - end - - it "should set the module as global if we parse the global manifests (ie module)" do - @parser.stubs(:split_module).returns("") - @parser.stubs(:parse_elements) - - @topcontainer.expects(:global=).with(true) - - @parser.scan_top_level(@topcontainer) - end - - it "should attach this module container to the toplevel container" do - @parser.stubs(:parse_elements) - - @container.expects(:add_module).with(RDoc::PuppetModule, "module").returns(@module) - - @parser.scan_top_level(@topcontainer) - end - - it "should defer ast parsing to parse_elements for this module" do - @parser.expects(:parse_elements).with(@module) - - @parser.scan_top_level(@topcontainer) - end - - it "should defer plugins parsing to parse_plugins for this module" do - @parser.input_file_name = "module/plugins/puppet/parser/function.rb" - - @parser.expects(:parse_plugins).with(@module) - - @parser.scan_top_level(@topcontainer) - end - end - - describe "when finding modules from filepath" do - before :each do - Puppet::Module.stubs(:modulepath).returns("/path/to/modules") - end - - it "should return the module name for modulized puppet manifests" do - File.stubs(:expand_path).returns("/path/to/module/manifests/init.pp") - File.stubs(:identical?).with("/path/to", "/path/to/modules").returns(true) - @parser.split_module("/path/to/modules/mymodule/manifests/init.pp").should == "module" - end - - it "should return for manifests not under module path" do - File.stubs(:expand_path).returns("/path/to/manifests/init.pp") - File.stubs(:identical?).returns(false) - @parser.split_module("/path/to/manifests/init.pp").should == "" - end - end - - describe "when parsing AST elements" do - before :each do - @klass = stub_everything 'klass', :file => "module/manifests/init.pp", :name => "myclass", :type => :hostclass - @definition = stub_everything 'definition', :file => "module/manifests/init.pp", :type => :definition, :name => "mydef" - @node = stub_everything 'node', :file => "module/manifests/init.pp", :type => :node, :name => "mynode" - - @resource_type_collection = Puppet::Resource::TypeCollection.new("env") - @parser.ast = @resource_type_collection - - @container = stub_everything 'container' - end - - it "should document classes in the parsed file" do - @resource_type_collection.add_hostclass(@klass) - - @parser.expects(:document_class).with("myclass", @klass, @container) - - @parser.parse_elements(@container) - end - - it "should not document class parsed in an other file" do - @klass.stubs(:file).returns("/not/same/path/file.pp") - @resource_type_collection.add_hostclass(@klass) - - @parser.expects(:document_class).with("myclass", @klass, @container).never - - @parser.parse_elements(@container) - end - - it "should document vardefs for the main class" do - @klass.stubs(:name).returns :main - @resource_type_collection.add_hostclass(@klass) - - code = stub 'code', :is_a? => false - @klass.stubs(:name).returns("") - @klass.stubs(:code).returns(code) - - @parser.expects(:scan_for_vardef).with(@container, code) - - @parser.parse_elements(@container) - end - - it "should document definitions in the parsed file" do - @resource_type_collection.add_definition(@definition) - - @parser.expects(:document_define).with("mydef", @definition, @container) - - @parser.parse_elements(@container) - end - - it "should not document definitions parsed in an other file" do - @definition.stubs(:file).returns("/not/same/path/file.pp") - @resource_type_collection.add_definition(@definition) - - @parser.expects(:document_define).with("mydef", @definition, @container).never - - @parser.parse_elements(@container) - end - - it "should document nodes in the parsed file" do - @resource_type_collection.add_node(@node) - - @parser.expects(:document_node).with("mynode", @node, @container) - - @parser.parse_elements(@container) - end - - it "should not document node parsed in an other file" do - @node.stubs(:file).returns("/not/same/path/file.pp") - @resource_type_collection.add_node(@node) - - @parser.expects(:document_node).with("mynode", @node, @container).never - - @parser.parse_elements(@container) - end - end - - describe "when documenting definition" do - before(:each) do - @define = stub_everything 'define', :arguments => [], :doc => "mydoc", :file => "file", :line => 42 - @class = stub_everything 'class' - @parser.stubs(:get_class_or_module).returns([@class, "mydef"]) - end - - it "should register a RDoc method to the current container" do - @class.expects(:add_method).with { |m| m.name == "mydef"} - @parser.document_define("mydef", @define, @class) - end - - it "should attach the documentation to this method" do - @class.expects(:add_method).with { |m| m.comment = "mydoc" } - - @parser.document_define("mydef", @define, @class) - end - - it "should produce a better error message on unhandled exception" do - @class.expects(:add_method).raises(ArgumentError) - - lambda { @parser.document_define("mydef", @define, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) - end - - it "should convert all definition parameter to string" do - arg = stub 'arg' - val = stub 'val' - - @define.stubs(:arguments).returns({arg => val}) - - arg.expects(:to_s).returns("arg") - val.expects(:to_s).returns("val") - - @parser.document_define("mydef", @define, @class) - end - end - - describe "when documenting nodes" do - before :each do - @code = stub_everything 'code' - @node = stub_everything 'node', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 - @rdoc_node = stub_everything 'rdocnode' - - @class = stub_everything 'class' - @class.stubs(:add_node).returns(@rdoc_node) - end - - it "should add a node to the current container" do - @class.expects(:add_node).with("mynode", "parent").returns(@rdoc_node) - @parser.document_node("mynode", @node, @class) - end - - it "should associate the node documentation to the rdoc node" do - @rdoc_node.expects(:comment=).with("mydoc") - @parser.document_node("mynode", @node, @class) - end - - it "should scan for include and require" do - @parser.expects(:scan_for_include_or_require).with(@rdoc_node, @code) - @parser.document_node("mynode", @node, @class) - end - - it "should scan for variable definition" do - @parser.expects(:scan_for_vardef).with(@rdoc_node, @code) - @parser.document_node("mynode", @node, @class) - end - - it "should scan for resources if needed" do - Puppet.settings.stubs(:[]).with(:document_all).returns(true) - @parser.expects(:scan_for_resource).with(@rdoc_node, @code) - @parser.document_node("mynode", @node, @class) - end - - it "should produce a better error message on unhandled exception" do - @class.stubs(:add_node).raises(ArgumentError) - - lambda { @parser.document_node("mynode", @node, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) - end - end - - describe "when documenting classes" do - before :each do - @code = stub_everything 'code' - @class = stub_everything 'class', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 - @rdoc_class = stub_everything 'rdoc-class' - - @module = stub_everything 'class' - @module.stubs(:add_class).returns(@rdoc_class) - @parser.stubs(:get_class_or_module).returns([@module, "myclass"]) - end - - it "should add a class to the current container" do - @module.expects(:add_class).with(RDoc::PuppetClass, "myclass", "parent").returns(@rdoc_class) - @parser.document_class("mynode", @class, @module) - end - - it "should set the superclass" do - @rdoc_class.expects(:superclass=).with("parent") - @parser.document_class("mynode", @class, @module) - end - - it "should associate the node documentation to the rdoc class" do - @rdoc_class.expects(:comment=).with("mydoc") - @parser.document_class("mynode", @class, @module) - end - - it "should scan for include and require" do - @parser.expects(:scan_for_include_or_require).with(@rdoc_class, @code) - @parser.document_class("mynode", @class, @module) - end - - it "should scan for resources if needed" do - Puppet.settings.stubs(:[]).with(:document_all).returns(true) - @parser.expects(:scan_for_resource).with(@rdoc_class, @code) - @parser.document_class("mynode", @class, @module) - end - - it "should produce a better error message on unhandled exception" do - @module.stubs(:add_class).raises(ArgumentError) - - lambda { @parser.document_class("mynode", @class, @module) }.should raise_error(Puppet::ParseError, /in file at line 42/) - end - end - - describe "when scanning for includes and requires" do - - def create_stmt(name) - stmt_value = stub "#{name}_value", :value => "myclass" - stmt = stub_everything 'stmt', :name => name, :arguments => [stmt_value], :doc => "mydoc" - stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) - stmt.stubs(:is_a?).with(Puppet::Parser::AST::Function).returns(true) - stmt - end - - before(:each) do - @class = stub_everything 'class' - @code = stub_everything 'code' - @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) - end - - it "should also scan mono-instruction code" do - @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } - - @parser.scan_for_include_or_require(@class,create_stmt("include")) - end - - it "should register recursively includes to the current container" do - @code.stubs(:children).returns([ create_stmt("include") ]) - - @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } - @parser.scan_for_include_or_require(@class, [@code]) - end - - it "should register requires to the current container" do - @code.stubs(:children).returns([ create_stmt("require") ]) - - @class.expects(:add_require).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } - @parser.scan_for_include_or_require(@class, [@code]) - end - end - - describe "when scanning for realized virtual resources" do - - def create_stmt - stmt_value = stub "resource_ref", :to_s => "File[\"/tmp/a\"]" - stmt = stub_everything 'stmt', :name => "realize", :arguments => [stmt_value], :doc => "mydoc" - stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) - stmt.stubs(:is_a?).with(Puppet::Parser::AST::Function).returns(true) - stmt - end - - before(:each) do - @class = stub_everything 'class' - @code = stub_everything 'code' - @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) - end - - it "should also scan mono-instruction code" do - @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } - - @parser.scan_for_realize(@class,create_stmt()) - end - - it "should register recursively includes to the current container" do - @code.stubs(:children).returns([ create_stmt() ]) - - @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } - @parser.scan_for_realize(@class, [@code]) - end - end - - describe "when scanning for variable definition" do - before :each do - @class = stub_everything 'class' - - @stmt = stub_everything 'stmt', :name => "myvar", :value => "myvalue", :doc => "mydoc" - @stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) - @stmt.stubs(:is_a?).with(Puppet::Parser::AST::VarDef).returns(true) - - @code = stub_everything 'code' - @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) - end - - it "should recursively register variables to the current container" do - @code.stubs(:children).returns([ @stmt ]) - - @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } - @parser.scan_for_vardef(@class, [ @code ]) - end - - it "should also scan mono-instruction code" do - @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } - - @parser.scan_for_vardef(@class, @stmt) - end - end - - describe "when scanning for resources" do - before :each do - @class = stub_everything 'class' - - param = stub 'params', :children => [] - @stmt = stub_everything 'stmt', :type => "File", :title => "myfile", :doc => "mydoc", :params => param - @stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) - @stmt.stubs(:is_a?).with(Puppet::Parser::AST::Resource).returns(true) - - @code = stub_everything 'code' - @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) - end - - it "should register a PuppetResource to the current container" do - @code.stubs(:children).returns([ @stmt ]) - - @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } - @parser.scan_for_resource(@class, [ @code ]) - end - - it "should also scan mono-instruction code" do - @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } - - @parser.scan_for_resource(@class, @stmt) - end - end - - describe "when parsing plugins" do - before :each do - @container = stub 'container' - end - - it "should delegate parsing custom facts to parse_facts" do - @parser = RDoc::Parser.new(@top_level, "module/manifests/plugins/puppet/facter/test.rb", nil, Options.instance, RDoc::Stats.new) - - @parser.expects(:parse_fact).with(@container) - @parser.parse_plugins(@container) - end - - it "should delegate parsing plugins to parse_plugins" do - @parser = RDoc::Parser.new(@top_level, "module/manifests/plugins/puppet/functions/test.rb", nil, Options.instance, RDoc::Stats.new) - - @parser.expects(:parse_puppet_plugin).with(@container) - @parser.parse_plugins(@container) - end - end - - describe "when parsing plugins" do - before :each do - @container = stub_everything 'container' - end - - it "should add custom functions to the container" do - File.stubs(:open).yields("# documentation - module Puppet::Parser::Functions - newfunction(:myfunc, :type => :rvalue) do |args| - File.dirname(args[0]) - end - end".split("\n")) - - @container.expects(:add_plugin).with do |plugin| - plugin.comment == "documentation\n" #and - plugin.name == "myfunc" - end - - @parser.parse_puppet_plugin(@container) - end - - it "should add custom types to the container" do - File.stubs(:open).yields("# documentation - Puppet::Type.newtype(:mytype) do - end".split("\n")) - - @container.expects(:add_plugin).with do |plugin| - plugin.comment == "documentation\n" #and - plugin.name == "mytype" - end - - @parser.parse_puppet_plugin(@container) - end - end - - describe "when parsing facts" do - before :each do - @container = stub_everything 'container' - File.stubs(:open).yields(["# documentation", "Facter.add('myfact') do", "confine :kernel => :linux", "end"]) - end - - it "should add facts to the container" do - @container.expects(:add_fact).with do |fact| - fact.comment == "documentation\n" and - fact.name == "myfact" - end - - @parser.parse_fact(@container) - end - - it "should add confine to the parsed facts" do - ourfact = nil - @container.expects(:add_fact).with do |fact| - ourfact = fact - true - end - - @parser.parse_fact(@container) - ourfact.confine.should == { :type => "kernel", :value => ":linux" } - end - end -end diff --git a/spec/unit/util/rdoc_spec.rb b/spec/unit/util/rdoc_spec.rb new file mode 100755 index 000000000..4417fca00 --- /dev/null +++ b/spec/unit/util/rdoc_spec.rb @@ -0,0 +1,165 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/rdoc' +require 'rdoc/rdoc' + +describe Puppet::Util::RDoc do + + describe "when generating RDoc HTML documentation" do + before :each do + @rdoc = stub_everything 'rdoc' + RDoc::RDoc.stubs(:new).returns(@rdoc) + end + + it "should tell the parser to ignore import" do + Puppet.expects(:[]=).with(:ignoreimport, true) + + Puppet::Util::RDoc.rdoc("output", []) + end + + it "should install the Puppet HTML Generator into RDoc generators" do + Puppet::Util::RDoc.rdoc("output", []) + + RDoc::RDoc::GENERATORS["puppet"].file_name.should == "puppet/util/rdoc/generators/puppet_generator.rb" + end + + it "should tell RDoc to generate documentation using the Puppet generator" do + @rdoc.expects(:document).with { |args| args.include?("--fmt") and args.include?("puppet") } + + Puppet::Util::RDoc.rdoc("output", []) + end + + it "should tell RDoc to be quiet" do + @rdoc.expects(:document).with { |args| args.include?("--quiet") } + + Puppet::Util::RDoc.rdoc("output", []) + end + + it "should pass charset to RDoc" do + @rdoc.expects(:document).with { |args| args.include?("--charset") and args.include?("utf-8") } + + Puppet::Util::RDoc.rdoc("output", [], "utf-8") + end + + it "should tell RDoc to force updates of indices" do + @rdoc.expects(:document).with { |args| args.include?("--force-update") } + + Puppet::Util::RDoc.rdoc("output", []) + end + + it "should tell RDoc to use the given outputdir" do + @rdoc.expects(:document).with { |args| args.include?("--op") and args.include?("myoutputdir") } + + Puppet::Util::RDoc.rdoc("myoutputdir", []) + end + + it "should tell RDoc to exclude .pp files under any modules//files section" do + @rdoc.expects(:document).with { |args| args.include?("--exclude") and args.include?("/modules/[^/]*/files/.*\.pp$") } + + Puppet::Util::RDoc.rdoc("myoutputdir", []) + end + + it "should give all the source directories to RDoc" do + @rdoc.expects(:document).with { |args| args.include?("sourcedir") } + + Puppet::Util::RDoc.rdoc("output", ["sourcedir"]) + end + end + + describe "when running a manifest documentation" do + it "should tell the parser to ignore import" do + Puppet.expects(:[]=).with(:ignoreimport, true) + + Puppet::Util::RDoc.manifestdoc([]) + end + + it "should puppet parse all given files" do + FileTest.stubs(:file?).returns(true) + Puppet::Util::RDoc.stubs(:output) + + parser = stub_everything + Puppet::Parser::Parser.stubs(:new).returns(parser) + + parser.expects(:file=).with("file") + parser.expects(:parse) + + Puppet::Util::RDoc.manifestdoc(["file"]) + end + + it "should call output for each parsed file" do + FileTest.stubs(:file?).returns(true) + + ast = stub_everything + parser = stub_everything + Puppet::Parser::Parser.stubs(:new).returns(parser) + parser.stubs(:parse).returns(ast) + + Puppet::Util::RDoc.expects(:output).with("file", ast) + + Puppet::Util::RDoc.manifestdoc(["file"]) + end + + describe "when outputing documentation" do + before :each do + @node = stub 'node', :file => "file", :line => 1, :doc => "" + @class = stub 'class', :file => "file", :line => 4, :doc => "" + @definition = stub 'definition', :file => "file", :line => 3, :doc => "" + @ast = stub 'ast', :nodes => { :node => @node }, :hostclasses => { :class => @class }, :definitions => { :definition => @definition } + end + + it "should output doc for ast nodes" do + @node.expects(:doc) + + Puppet::Util::RDoc.output("file", @ast) + end + + it "should output doc for ast classes" do + @class.expects(:doc) + + Puppet::Util::RDoc.output("file", @ast) + end + + it "should output doc for ast definitions" do + @definition.expects(:doc) + + Puppet::Util::RDoc.output("file", @ast) + end + + it "should output doc in order of increasing line number" do + byline = sequence('byline') + @node.expects(:doc).in_sequence(byline) + @definition.expects(:doc).in_sequence(byline) + @class.expects(:doc).in_sequence(byline) + + Puppet::Util::RDoc.output("file", @ast) + end + + it "should not output documentation of ast object of another node" do + klass = stub 'otherclass', :file => "otherfile", :line => 12, :doc => "" + @ast.stubs(:hostclasses).returns({ :otherclass => klass }) + + klass.expects(:doc).never + + Puppet::Util::RDoc.output("file", @ast) + end + + it "should output resource documentation if needed" do + Puppet.settings.stubs(:[]).with(:document_all).returns(true) + [@node,@definition].each do |o| + o.stubs(:code).returns([]) + end + + resource = stub_everything 'resource', :line => 1 + resource.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) + resource.stubs(:is_a?).with(Puppet::Parser::AST::Resource).returns(true) + @class.stubs(:code).returns([resource]) + + resource.expects(:doc) + + Puppet::Util::RDoc.output("file", @ast) + end + end + end +end diff --git a/spec/unit/util/rdoc_spec_spec.rb b/spec/unit/util/rdoc_spec_spec.rb deleted file mode 100755 index 4417fca00..000000000 --- a/spec/unit/util/rdoc_spec_spec.rb +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/rdoc' -require 'rdoc/rdoc' - -describe Puppet::Util::RDoc do - - describe "when generating RDoc HTML documentation" do - before :each do - @rdoc = stub_everything 'rdoc' - RDoc::RDoc.stubs(:new).returns(@rdoc) - end - - it "should tell the parser to ignore import" do - Puppet.expects(:[]=).with(:ignoreimport, true) - - Puppet::Util::RDoc.rdoc("output", []) - end - - it "should install the Puppet HTML Generator into RDoc generators" do - Puppet::Util::RDoc.rdoc("output", []) - - RDoc::RDoc::GENERATORS["puppet"].file_name.should == "puppet/util/rdoc/generators/puppet_generator.rb" - end - - it "should tell RDoc to generate documentation using the Puppet generator" do - @rdoc.expects(:document).with { |args| args.include?("--fmt") and args.include?("puppet") } - - Puppet::Util::RDoc.rdoc("output", []) - end - - it "should tell RDoc to be quiet" do - @rdoc.expects(:document).with { |args| args.include?("--quiet") } - - Puppet::Util::RDoc.rdoc("output", []) - end - - it "should pass charset to RDoc" do - @rdoc.expects(:document).with { |args| args.include?("--charset") and args.include?("utf-8") } - - Puppet::Util::RDoc.rdoc("output", [], "utf-8") - end - - it "should tell RDoc to force updates of indices" do - @rdoc.expects(:document).with { |args| args.include?("--force-update") } - - Puppet::Util::RDoc.rdoc("output", []) - end - - it "should tell RDoc to use the given outputdir" do - @rdoc.expects(:document).with { |args| args.include?("--op") and args.include?("myoutputdir") } - - Puppet::Util::RDoc.rdoc("myoutputdir", []) - end - - it "should tell RDoc to exclude .pp files under any modules//files section" do - @rdoc.expects(:document).with { |args| args.include?("--exclude") and args.include?("/modules/[^/]*/files/.*\.pp$") } - - Puppet::Util::RDoc.rdoc("myoutputdir", []) - end - - it "should give all the source directories to RDoc" do - @rdoc.expects(:document).with { |args| args.include?("sourcedir") } - - Puppet::Util::RDoc.rdoc("output", ["sourcedir"]) - end - end - - describe "when running a manifest documentation" do - it "should tell the parser to ignore import" do - Puppet.expects(:[]=).with(:ignoreimport, true) - - Puppet::Util::RDoc.manifestdoc([]) - end - - it "should puppet parse all given files" do - FileTest.stubs(:file?).returns(true) - Puppet::Util::RDoc.stubs(:output) - - parser = stub_everything - Puppet::Parser::Parser.stubs(:new).returns(parser) - - parser.expects(:file=).with("file") - parser.expects(:parse) - - Puppet::Util::RDoc.manifestdoc(["file"]) - end - - it "should call output for each parsed file" do - FileTest.stubs(:file?).returns(true) - - ast = stub_everything - parser = stub_everything - Puppet::Parser::Parser.stubs(:new).returns(parser) - parser.stubs(:parse).returns(ast) - - Puppet::Util::RDoc.expects(:output).with("file", ast) - - Puppet::Util::RDoc.manifestdoc(["file"]) - end - - describe "when outputing documentation" do - before :each do - @node = stub 'node', :file => "file", :line => 1, :doc => "" - @class = stub 'class', :file => "file", :line => 4, :doc => "" - @definition = stub 'definition', :file => "file", :line => 3, :doc => "" - @ast = stub 'ast', :nodes => { :node => @node }, :hostclasses => { :class => @class }, :definitions => { :definition => @definition } - end - - it "should output doc for ast nodes" do - @node.expects(:doc) - - Puppet::Util::RDoc.output("file", @ast) - end - - it "should output doc for ast classes" do - @class.expects(:doc) - - Puppet::Util::RDoc.output("file", @ast) - end - - it "should output doc for ast definitions" do - @definition.expects(:doc) - - Puppet::Util::RDoc.output("file", @ast) - end - - it "should output doc in order of increasing line number" do - byline = sequence('byline') - @node.expects(:doc).in_sequence(byline) - @definition.expects(:doc).in_sequence(byline) - @class.expects(:doc).in_sequence(byline) - - Puppet::Util::RDoc.output("file", @ast) - end - - it "should not output documentation of ast object of another node" do - klass = stub 'otherclass', :file => "otherfile", :line => 12, :doc => "" - @ast.stubs(:hostclasses).returns({ :otherclass => klass }) - - klass.expects(:doc).never - - Puppet::Util::RDoc.output("file", @ast) - end - - it "should output resource documentation if needed" do - Puppet.settings.stubs(:[]).with(:document_all).returns(true) - [@node,@definition].each do |o| - o.stubs(:code).returns([]) - end - - resource = stub_everything 'resource', :line => 1 - resource.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) - resource.stubs(:is_a?).with(Puppet::Parser::AST::Resource).returns(true) - @class.stubs(:code).returns([resource]) - - resource.expects(:doc) - - Puppet::Util::RDoc.output("file", @ast) - end - end - end -end diff --git a/spec/unit/util/reference_serializer_spec.rb b/spec/unit/util/reference_serializer_spec.rb new file mode 100644 index 000000000..cfabec4cc --- /dev/null +++ b/spec/unit/util/reference_serializer_spec.rb @@ -0,0 +1,52 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/util/rails/reference_serializer' + +class SerializeTester + include Puppet::Util::ReferenceSerializer +end + +describe Puppet::Util::ReferenceSerializer do + before do + @tester = SerializeTester.new + end + + describe "when serializing" do + it "should yaml-dump resource references" do + ref = Puppet::Resource.new("file", "/foo") + @tester.serialize_value(ref).should =~ /^---/ + end + + it "should convert the boolean 'true' into the string 'true'" do + @tester.serialize_value(true).should == "true" + end + + it "should convert the boolean 'false' into the string 'false'" do + @tester.serialize_value(false).should == "false" + end + + it "should return all other values" do + @tester.serialize_value("foo").should == "foo" + end + end + + describe "when unserializing" do + it "should yaml-load values that look like yaml" do + yaml = YAML.dump(%w{a b c}) + @tester.unserialize_value(yaml).should == %w{a b c} + end + + it "should convert the string 'true' into the boolean 'true'" do + @tester.unserialize_value("true").should == true + end + + it "should convert the string 'false' into the boolean 'false'" do + @tester.unserialize_value("false").should == false + end + + it "should return all other values" do + @tester.unserialize_value("foo").should == "foo" + end + end +end diff --git a/spec/unit/util/reference_serializer_spec_spec.rb b/spec/unit/util/reference_serializer_spec_spec.rb deleted file mode 100644 index cfabec4cc..000000000 --- a/spec/unit/util/reference_serializer_spec_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/util/rails/reference_serializer' - -class SerializeTester - include Puppet::Util::ReferenceSerializer -end - -describe Puppet::Util::ReferenceSerializer do - before do - @tester = SerializeTester.new - end - - describe "when serializing" do - it "should yaml-dump resource references" do - ref = Puppet::Resource.new("file", "/foo") - @tester.serialize_value(ref).should =~ /^---/ - end - - it "should convert the boolean 'true' into the string 'true'" do - @tester.serialize_value(true).should == "true" - end - - it "should convert the boolean 'false' into the string 'false'" do - @tester.serialize_value(false).should == "false" - end - - it "should return all other values" do - @tester.serialize_value("foo").should == "foo" - end - end - - describe "when unserializing" do - it "should yaml-load values that look like yaml" do - yaml = YAML.dump(%w{a b c}) - @tester.unserialize_value(yaml).should == %w{a b c} - end - - it "should convert the string 'true' into the boolean 'true'" do - @tester.unserialize_value("true").should == true - end - - it "should convert the string 'false' into the boolean 'false'" do - @tester.unserialize_value("false").should == false - end - - it "should return all other values" do - @tester.unserialize_value("foo").should == "foo" - end - end -end diff --git a/spec/unit/util/resource_template_spec.rb b/spec/unit/util/resource_template_spec.rb new file mode 100755 index 000000000..b4d529e5d --- /dev/null +++ b/spec/unit/util/resource_template_spec.rb @@ -0,0 +1,58 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/resource_template' + +describe Puppet::Util::ResourceTemplate do + describe "when initializing" do + it "should fail if the template does not exist" do + FileTest.expects(:exist?).with("/my/template").returns false + lambda { Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) }.should raise_error(ArgumentError) + end + + it "should not create the ERB template" do + ERB.expects(:new).never + FileTest.expects(:exist?).with("/my/template").returns true + Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) + end + end + + describe "when evaluating" do + before do + FileTest.stubs(:exist?).returns true + File.stubs(:read).returns "eh" + + @template = stub 'template', :result => nil + ERB.stubs(:new).returns @template + + @resource = mock 'resource' + @wrapper = Puppet::Util::ResourceTemplate.new("/my/template", @resource) + end + + it "should set all of the resource's parameters as instance variables" do + @resource.expects(:to_hash).returns(:one => "uno", :two => "dos") + @template.expects(:result).with do |bind| + eval("@one", bind) == "uno" and eval("@two", bind) == "dos" + end + @wrapper.evaluate + end + + it "should create a template instance with the contents of the file" do + File.expects(:read).with("/my/template").returns "yay" + ERB.expects(:new).with("yay", 0, "-").returns(@template) + + @wrapper.stubs :set_resource_variables + + @wrapper.evaluate + end + + it "should return the result of the template" do + @wrapper.stubs :set_resource_variables + + @wrapper.expects(:binding).returns "mybinding" + @template.expects(:result).with("mybinding").returns "myresult" + @wrapper.evaluate.should == "myresult" + end + end +end diff --git a/spec/unit/util/resource_template_spec_spec.rb b/spec/unit/util/resource_template_spec_spec.rb deleted file mode 100755 index b4d529e5d..000000000 --- a/spec/unit/util/resource_template_spec_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/util/resource_template' - -describe Puppet::Util::ResourceTemplate do - describe "when initializing" do - it "should fail if the template does not exist" do - FileTest.expects(:exist?).with("/my/template").returns false - lambda { Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) }.should raise_error(ArgumentError) - end - - it "should not create the ERB template" do - ERB.expects(:new).never - FileTest.expects(:exist?).with("/my/template").returns true - Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) - end - end - - describe "when evaluating" do - before do - FileTest.stubs(:exist?).returns true - File.stubs(:read).returns "eh" - - @template = stub 'template', :result => nil - ERB.stubs(:new).returns @template - - @resource = mock 'resource' - @wrapper = Puppet::Util::ResourceTemplate.new("/my/template", @resource) - end - - it "should set all of the resource's parameters as instance variables" do - @resource.expects(:to_hash).returns(:one => "uno", :two => "dos") - @template.expects(:result).with do |bind| - eval("@one", bind) == "uno" and eval("@two", bind) == "dos" - end - @wrapper.evaluate - end - - it "should create a template instance with the contents of the file" do - File.expects(:read).with("/my/template").returns "yay" - ERB.expects(:new).with("yay", 0, "-").returns(@template) - - @wrapper.stubs :set_resource_variables - - @wrapper.evaluate - end - - it "should return the result of the template" do - @wrapper.stubs :set_resource_variables - - @wrapper.expects(:binding).returns "mybinding" - @template.expects(:result).with("mybinding").returns "myresult" - @wrapper.evaluate.should == "myresult" - end - end -end diff --git a/spec/unit/util/selinux_spec.rb b/spec/unit/util/selinux_spec.rb new file mode 100755 index 000000000..88f4ac809 --- /dev/null +++ b/spec/unit/util/selinux_spec.rb @@ -0,0 +1,280 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/selinux' +include Puppet::Util::SELinux + +unless defined?(Selinux) + module Selinux + def self.is_selinux_enabled + false + end + end +end + +describe Puppet::Util::SELinux do + + describe "selinux_support?" do + before do + end + it "should return :true if this system has SELinux enabled" do + Selinux.expects(:is_selinux_enabled).returns 1 + selinux_support?.should be_true + end + + it "should return :false if this system lacks SELinux" do + Selinux.expects(:is_selinux_enabled).returns 0 + selinux_support?.should be_false + end + + it "should return nil if /proc/mounts does not exist" do + File.stubs(:open).with("/proc/mounts").raises("No such file or directory - /proc/mounts") + read_mounts.should == nil + end + end + + describe "filesystem detection" do + before :each do + fh = stub 'fh', :close => nil + File.stubs(:open).with("/proc/mounts").returns fh + fh.expects(:read_nonblock).times(2).returns("rootfs / rootfs rw 0 0\n/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\n/dev /dev tmpfs rw,relatime,mode=755 0 0\n/proc /proc proc rw,relatime 0 0\n/sys /sys sysfs rw,relatime 0 0\n192.168.1.1:/var/export /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,nointr,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.1,mountvers=3,mountproto=udp,addr=192.168.1.1 0 0\n").then.raises EOFError + end + + it "should parse the contents of /proc/mounts" do + read_mounts().should == { + '/' => 'ext3', + '/sys' => 'sysfs', + '/mnt/nfs' => 'nfs', + '/proc' => 'proc', + '/dev' => 'tmpfs' } + end + + it "should match a path on / to ext3" do + find_fs('/etc/puppet/testfile').should == "ext3" + end + + it "should match a path on /mnt/nfs to nfs" do + find_fs('/mnt/nfs/testfile/foobar').should == "nfs" + end + + it "should reture true for a capable filesystem" do + selinux_label_support?('/etc/puppet/testfile').should be_true + end + + it "should return false for a noncapable filesystem" do + selinux_label_support?('/mnt/nfs/testfile').should be_false + end + + it "should follow symlinks when determining file systems" do + self.stubs(:realpath).with('/mnt/symlink/testfile').returns('/mnt/nfs/dest/testfile') + + selinux_label_support?('/mnt/symlink/testfile').should be_false + end + + end + + describe "realpath" do + it "should handle files that don't exist" do + + # Since I'm stubbing Pathname.new for this test, + # I need to also stub the internal calls to Pathname.new, + # which happen in Pathname.dirname and Parthname.basename + # I want those to return real Pathname objects, + # so I'm creating them before the stub is in place. + realpaths = Hash.new {|hash, path| hash[path] = Pathname.new(path) } + paths = ['symlink', '/mnt'] + paths.each { |path| realpaths[path] } + + realpaths['/mnt/symlink'] = stubs "Pathname" + realpaths['/mnt/symlink'].stubs(:realpath).returns(realpaths['/mnt/nfs/dest']) + realpaths['/mnt/symlink'].stubs(:exist?).returns(true) + + realpaths['/mnt/symlink/nonexistant'] = stubs "Pathname" + realpaths['/mnt/symlink/nonexistant'].stubs(:realpath).raises(Errno::ENOENT) + realpaths['/mnt/symlink/nonexistant'].stubs(:exist?).returns(false) + realpaths['/mnt/symlink/nonexistant'].stubs(:dirname).returns(realpaths['/mnt/symlink']) + realpaths['/mnt/symlink/nonexistant'].stubs(:basename).returns(realpaths['nonexistant']) + + realpaths.each do |path, value| + Pathname.stubs(:new).with(path).returns(value) + end + + realpath('/mnt/symlink/nonexistant').should == '/mnt/nfs/dest/nonexistant' + end + end + + describe "get_selinux_current_context" do + it "should return nil if no SELinux support" do + self.expects(:selinux_support?).returns false + get_selinux_current_context("/foo").should be_nil + end + + it "should return a context" do + self.expects(:selinux_support?).returns true + Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"] + get_selinux_current_context("/foo").should == "user_u:role_r:type_t:s0" + end + + it "should return nil if lgetfilecon fails" do + self.expects(:selinux_support?).returns true + Selinux.expects(:lgetfilecon).with("/foo").returns -1 + get_selinux_current_context("/foo").should be_nil + end + end + + describe "get_selinux_default_context" do + it "should return nil if no SELinux support" do + self.expects(:selinux_support?).returns false + get_selinux_default_context("/foo").should be_nil + end + + it "should return a context if a default context exists" do + self.expects(:selinux_support?).returns true + fstat = stub 'File::Stat', :mode => 0 + File.expects(:lstat).with("/foo").returns fstat + self.expects(:find_fs).with("/foo").returns "ext3" + Selinux.expects(:matchpathcon).with("/foo", 0).returns [0, "user_u:role_r:type_t:s0"] + get_selinux_default_context("/foo").should == "user_u:role_r:type_t:s0" + end + + it "should return nil if matchpathcon returns failure" do + self.expects(:selinux_support?).returns true + fstat = stub 'File::Stat', :mode => 0 + File.expects(:lstat).with("/foo").returns fstat + self.expects(:find_fs).with("/foo").returns "ext3" + Selinux.expects(:matchpathcon).with("/foo", 0).returns -1 + get_selinux_default_context("/foo").should be_nil + end + + it "should return nil if selinux_label_support returns false" do + self.expects(:selinux_support?).returns true + self.expects(:find_fs).with("/foo").returns "nfs" + get_selinux_default_context("/foo").should be_nil + end + + end + + describe "parse_selinux_context" do + it "should return nil if no context is passed" do + parse_selinux_context(:seluser, nil).should be_nil + end + + it "should return nil if the context is 'unlabeled'" do + parse_selinux_context(:seluser, "unlabeled").should be_nil + end + + it "should return the user type when called with :seluser" do + parse_selinux_context(:seluser, "user_u:role_r:type_t:s0").should == "user_u" + end + + it "should return the role type when called with :selrole" do + parse_selinux_context(:selrole, "user_u:role_r:type_t:s0").should == "role_r" + end + + it "should return the type type when called with :seltype" do + parse_selinux_context(:seltype, "user_u:role_r:type_t:s0").should == "type_t" + end + + it "should return nil for :selrange when no range is returned" do + parse_selinux_context(:selrange, "user_u:role_r:type_t").should be_nil + end + + it "should return the range type when called with :selrange" do + parse_selinux_context(:selrange, "user_u:role_r:type_t:s0").should == "s0" + end + + describe "with a variety of SELinux range formats" do + ['s0', 's0:c3', 's0:c3.c123', 's0:c3,c5,c8', 'TopSecret', 'TopSecret,Classified', 'Patient_Record'].each do |range| + it "should parse range '#{range}'" do + parse_selinux_context(:selrange, "user_u:role_r:type_t:#{range}").should == range + end + end + end + end + + describe "set_selinux_context" do + before :each do + fh = stub 'fh', :close => nil + File.stubs(:open).with("/proc/mounts").returns fh + fh.stubs(:read_nonblock).returns( + "rootfs / rootfs rw 0 0\n"+ + "/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\n"+ + "/dev /dev tmpfs rw,relatime,mode=755 0 0\n"+ + "/proc /proc proc rw,relatime 0 0\n"+ + "/sys /sys sysfs rw,relatime 0 0\n" + ).then.raises EOFError + end + + it "should return nil if there is no SELinux support" do + self.expects(:selinux_support?).returns false + set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil + end + + it "should return nil if selinux_label_support returns false" do + self.expects(:selinux_support?).returns true + self.expects(:selinux_label_support?).with("/foo").returns false + set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil + end + + it "should use lsetfilecon to set a context" do + self.expects(:selinux_support?).returns true + Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 + set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_true + end + + it "should use lsetfilecon to set user_u user context" do + self.expects(:selinux_support?).returns true + Selinux.expects(:lgetfilecon).with("/foo").returns [0, "foo:role_r:type_t:s0"] + Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 + set_selinux_context("/foo", "user_u", :seluser).should be_true + end + + it "should use lsetfilecon to set role_r role context" do + self.expects(:selinux_support?).returns true + Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:foo:type_t:s0"] + Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 + set_selinux_context("/foo", "role_r", :selrole).should be_true + end + + it "should use lsetfilecon to set type_t type context" do + self.expects(:selinux_support?).returns true + Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:foo:s0"] + Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 + set_selinux_context("/foo", "type_t", :seltype).should be_true + end + + it "should use lsetfilecon to set s0:c3,c5 range context" do + self.expects(:selinux_support?).returns true + Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"] + Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0:c3,c5").returns 0 + set_selinux_context("/foo", "s0:c3,c5", :selrange).should be_true + end + end + + describe "set_selinux_default_context" do + it "should return nil if there is no SELinux support" do + self.expects(:selinux_support?).returns false + set_selinux_default_context("/foo").should be_nil + end + + it "should return nil if no default context exists" do + self.expects(:get_selinux_default_context).with("/foo").returns nil + set_selinux_default_context("/foo").should be_nil + end + + it "should do nothing and return nil if the current context matches the default context" do + self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" + self.expects(:get_selinux_current_context).with("/foo").returns "user_u:role_r:type_t" + set_selinux_default_context("/foo").should be_nil + end + + it "should set and return the default context if current and default do not match" do + self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" + self.expects(:get_selinux_current_context).with("/foo").returns "olduser_u:role_r:type_t" + self.expects(:set_selinux_context).with("/foo", "user_u:role_r:type_t").returns true + set_selinux_default_context("/foo").should == "user_u:role_r:type_t" + end + end + +end diff --git a/spec/unit/util/selinux_spec_spec.rb b/spec/unit/util/selinux_spec_spec.rb deleted file mode 100755 index 88f4ac809..000000000 --- a/spec/unit/util/selinux_spec_spec.rb +++ /dev/null @@ -1,280 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/util/selinux' -include Puppet::Util::SELinux - -unless defined?(Selinux) - module Selinux - def self.is_selinux_enabled - false - end - end -end - -describe Puppet::Util::SELinux do - - describe "selinux_support?" do - before do - end - it "should return :true if this system has SELinux enabled" do - Selinux.expects(:is_selinux_enabled).returns 1 - selinux_support?.should be_true - end - - it "should return :false if this system lacks SELinux" do - Selinux.expects(:is_selinux_enabled).returns 0 - selinux_support?.should be_false - end - - it "should return nil if /proc/mounts does not exist" do - File.stubs(:open).with("/proc/mounts").raises("No such file or directory - /proc/mounts") - read_mounts.should == nil - end - end - - describe "filesystem detection" do - before :each do - fh = stub 'fh', :close => nil - File.stubs(:open).with("/proc/mounts").returns fh - fh.expects(:read_nonblock).times(2).returns("rootfs / rootfs rw 0 0\n/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\n/dev /dev tmpfs rw,relatime,mode=755 0 0\n/proc /proc proc rw,relatime 0 0\n/sys /sys sysfs rw,relatime 0 0\n192.168.1.1:/var/export /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,nointr,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.1,mountvers=3,mountproto=udp,addr=192.168.1.1 0 0\n").then.raises EOFError - end - - it "should parse the contents of /proc/mounts" do - read_mounts().should == { - '/' => 'ext3', - '/sys' => 'sysfs', - '/mnt/nfs' => 'nfs', - '/proc' => 'proc', - '/dev' => 'tmpfs' } - end - - it "should match a path on / to ext3" do - find_fs('/etc/puppet/testfile').should == "ext3" - end - - it "should match a path on /mnt/nfs to nfs" do - find_fs('/mnt/nfs/testfile/foobar').should == "nfs" - end - - it "should reture true for a capable filesystem" do - selinux_label_support?('/etc/puppet/testfile').should be_true - end - - it "should return false for a noncapable filesystem" do - selinux_label_support?('/mnt/nfs/testfile').should be_false - end - - it "should follow symlinks when determining file systems" do - self.stubs(:realpath).with('/mnt/symlink/testfile').returns('/mnt/nfs/dest/testfile') - - selinux_label_support?('/mnt/symlink/testfile').should be_false - end - - end - - describe "realpath" do - it "should handle files that don't exist" do - - # Since I'm stubbing Pathname.new for this test, - # I need to also stub the internal calls to Pathname.new, - # which happen in Pathname.dirname and Parthname.basename - # I want those to return real Pathname objects, - # so I'm creating them before the stub is in place. - realpaths = Hash.new {|hash, path| hash[path] = Pathname.new(path) } - paths = ['symlink', '/mnt'] - paths.each { |path| realpaths[path] } - - realpaths['/mnt/symlink'] = stubs "Pathname" - realpaths['/mnt/symlink'].stubs(:realpath).returns(realpaths['/mnt/nfs/dest']) - realpaths['/mnt/symlink'].stubs(:exist?).returns(true) - - realpaths['/mnt/symlink/nonexistant'] = stubs "Pathname" - realpaths['/mnt/symlink/nonexistant'].stubs(:realpath).raises(Errno::ENOENT) - realpaths['/mnt/symlink/nonexistant'].stubs(:exist?).returns(false) - realpaths['/mnt/symlink/nonexistant'].stubs(:dirname).returns(realpaths['/mnt/symlink']) - realpaths['/mnt/symlink/nonexistant'].stubs(:basename).returns(realpaths['nonexistant']) - - realpaths.each do |path, value| - Pathname.stubs(:new).with(path).returns(value) - end - - realpath('/mnt/symlink/nonexistant').should == '/mnt/nfs/dest/nonexistant' - end - end - - describe "get_selinux_current_context" do - it "should return nil if no SELinux support" do - self.expects(:selinux_support?).returns false - get_selinux_current_context("/foo").should be_nil - end - - it "should return a context" do - self.expects(:selinux_support?).returns true - Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"] - get_selinux_current_context("/foo").should == "user_u:role_r:type_t:s0" - end - - it "should return nil if lgetfilecon fails" do - self.expects(:selinux_support?).returns true - Selinux.expects(:lgetfilecon).with("/foo").returns -1 - get_selinux_current_context("/foo").should be_nil - end - end - - describe "get_selinux_default_context" do - it "should return nil if no SELinux support" do - self.expects(:selinux_support?).returns false - get_selinux_default_context("/foo").should be_nil - end - - it "should return a context if a default context exists" do - self.expects(:selinux_support?).returns true - fstat = stub 'File::Stat', :mode => 0 - File.expects(:lstat).with("/foo").returns fstat - self.expects(:find_fs).with("/foo").returns "ext3" - Selinux.expects(:matchpathcon).with("/foo", 0).returns [0, "user_u:role_r:type_t:s0"] - get_selinux_default_context("/foo").should == "user_u:role_r:type_t:s0" - end - - it "should return nil if matchpathcon returns failure" do - self.expects(:selinux_support?).returns true - fstat = stub 'File::Stat', :mode => 0 - File.expects(:lstat).with("/foo").returns fstat - self.expects(:find_fs).with("/foo").returns "ext3" - Selinux.expects(:matchpathcon).with("/foo", 0).returns -1 - get_selinux_default_context("/foo").should be_nil - end - - it "should return nil if selinux_label_support returns false" do - self.expects(:selinux_support?).returns true - self.expects(:find_fs).with("/foo").returns "nfs" - get_selinux_default_context("/foo").should be_nil - end - - end - - describe "parse_selinux_context" do - it "should return nil if no context is passed" do - parse_selinux_context(:seluser, nil).should be_nil - end - - it "should return nil if the context is 'unlabeled'" do - parse_selinux_context(:seluser, "unlabeled").should be_nil - end - - it "should return the user type when called with :seluser" do - parse_selinux_context(:seluser, "user_u:role_r:type_t:s0").should == "user_u" - end - - it "should return the role type when called with :selrole" do - parse_selinux_context(:selrole, "user_u:role_r:type_t:s0").should == "role_r" - end - - it "should return the type type when called with :seltype" do - parse_selinux_context(:seltype, "user_u:role_r:type_t:s0").should == "type_t" - end - - it "should return nil for :selrange when no range is returned" do - parse_selinux_context(:selrange, "user_u:role_r:type_t").should be_nil - end - - it "should return the range type when called with :selrange" do - parse_selinux_context(:selrange, "user_u:role_r:type_t:s0").should == "s0" - end - - describe "with a variety of SELinux range formats" do - ['s0', 's0:c3', 's0:c3.c123', 's0:c3,c5,c8', 'TopSecret', 'TopSecret,Classified', 'Patient_Record'].each do |range| - it "should parse range '#{range}'" do - parse_selinux_context(:selrange, "user_u:role_r:type_t:#{range}").should == range - end - end - end - end - - describe "set_selinux_context" do - before :each do - fh = stub 'fh', :close => nil - File.stubs(:open).with("/proc/mounts").returns fh - fh.stubs(:read_nonblock).returns( - "rootfs / rootfs rw 0 0\n"+ - "/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\n"+ - "/dev /dev tmpfs rw,relatime,mode=755 0 0\n"+ - "/proc /proc proc rw,relatime 0 0\n"+ - "/sys /sys sysfs rw,relatime 0 0\n" - ).then.raises EOFError - end - - it "should return nil if there is no SELinux support" do - self.expects(:selinux_support?).returns false - set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil - end - - it "should return nil if selinux_label_support returns false" do - self.expects(:selinux_support?).returns true - self.expects(:selinux_label_support?).with("/foo").returns false - set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil - end - - it "should use lsetfilecon to set a context" do - self.expects(:selinux_support?).returns true - Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 - set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_true - end - - it "should use lsetfilecon to set user_u user context" do - self.expects(:selinux_support?).returns true - Selinux.expects(:lgetfilecon).with("/foo").returns [0, "foo:role_r:type_t:s0"] - Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 - set_selinux_context("/foo", "user_u", :seluser).should be_true - end - - it "should use lsetfilecon to set role_r role context" do - self.expects(:selinux_support?).returns true - Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:foo:type_t:s0"] - Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 - set_selinux_context("/foo", "role_r", :selrole).should be_true - end - - it "should use lsetfilecon to set type_t type context" do - self.expects(:selinux_support?).returns true - Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:foo:s0"] - Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 - set_selinux_context("/foo", "type_t", :seltype).should be_true - end - - it "should use lsetfilecon to set s0:c3,c5 range context" do - self.expects(:selinux_support?).returns true - Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"] - Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0:c3,c5").returns 0 - set_selinux_context("/foo", "s0:c3,c5", :selrange).should be_true - end - end - - describe "set_selinux_default_context" do - it "should return nil if there is no SELinux support" do - self.expects(:selinux_support?).returns false - set_selinux_default_context("/foo").should be_nil - end - - it "should return nil if no default context exists" do - self.expects(:get_selinux_default_context).with("/foo").returns nil - set_selinux_default_context("/foo").should be_nil - end - - it "should do nothing and return nil if the current context matches the default context" do - self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" - self.expects(:get_selinux_current_context).with("/foo").returns "user_u:role_r:type_t" - set_selinux_default_context("/foo").should be_nil - end - - it "should set and return the default context if current and default do not match" do - self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" - self.expects(:get_selinux_current_context).with("/foo").returns "olduser_u:role_r:type_t" - self.expects(:set_selinux_context).with("/foo", "user_u:role_r:type_t").returns true - set_selinux_default_context("/foo").should == "user_u:role_r:type_t" - end - end - -end diff --git a/spec/unit/util/settings/file_setting_spec.rb b/spec/unit/util/settings/file_setting_spec.rb new file mode 100755 index 000000000..a6b7f024b --- /dev/null +++ b/spec/unit/util/settings/file_setting_spec.rb @@ -0,0 +1,256 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/util/settings' +require 'puppet/util/settings/file_setting' + +describe Puppet::Util::Settings::FileSetting do + FileSetting = Puppet::Util::Settings::FileSetting + + before do + @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" + end + + describe "when determining whether the service user should be used" do + before do + @settings = mock 'settings' + @settings.stubs(:[]).with(:mkusers).returns false + @settings.stubs(:service_user_available?).returns true + end + + it "should be true if the service user is available" do + @settings.expects(:service_user_available?).returns true + setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") + setting.should be_use_service_user + end + + it "should be true if 'mkusers' is set" do + @settings.expects(:[]).with(:mkusers).returns true + setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") + setting.should be_use_service_user + end + + it "should be false if the service user is not available and 'mkusers' is unset" do + setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") + setting.should be_use_service_user + end + end + + describe "when setting the owner" do + it "should allow the file to be owned by root" do + root_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "root", :desc => "a setting") } + root_owner.should_not raise_error + end + + it "should allow the file to be owned by the service user" do + service_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "service", :desc => "a setting") } + service_owner.should_not raise_error + end + + it "should allow the ownership of the file to be unspecified" do + no_owner = lambda { FileSetting.new(:settings => mock("settings"), :desc => "a setting") } + no_owner.should_not raise_error + end + + it "should not allow other owners" do + invalid_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "invalid", :desc => "a setting") } + invalid_owner.should raise_error(FileSetting::SettingError) + end + end + + describe "when reading the owner" do + it "should be root when the setting specifies root" do + setting = FileSetting.new(:settings => mock("settings"), :owner => "root", :desc => "a setting") + setting.owner.should == "root" + end + + it "should be the owner of the service when the setting specifies service and the service user should be used" do + settings = mock("settings") + settings.stubs(:[]).returns "the_service" + + setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") + setting.expects(:use_service_user?).returns true + setting.owner.should == "the_service" + end + + it "should be the root when the setting specifies service and the service user should not be used" do + settings = mock("settings") + settings.stubs(:[]).returns "the_service" + + setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") + setting.expects(:use_service_user?).returns false + setting.owner.should == "root" + end + + it "should be nil when the owner is unspecified" do + FileSetting.new(:settings => mock("settings"), :desc => "a setting").owner.should be_nil + end + end + + describe "when setting the group" do + it "should allow the group to be service" do + service_group = lambda { FileSetting.new(:settings => mock("settings"), :group => "service", :desc => "a setting") } + service_group.should_not raise_error + end + + it "should allow the group to be unspecified" do + no_group = lambda { FileSetting.new(:settings => mock("settings"), :desc => "a setting") } + no_group.should_not raise_error + end + + it "should not allow invalid groups" do + invalid_group = lambda { FileSetting.new(:settings => mock("settings"), :group => "invalid", :desc => "a setting") } + invalid_group.should raise_error(FileSetting::SettingError) + end + end + + describe "when reading the group" do + it "should be service when the setting specifies service" do + setting = FileSetting.new(:settings => mock("settings", :[] => "the_service"), :group => "service", :desc => "a setting") + setting.group.should == "the_service" + end + + it "should be nil when the group is unspecified" do + FileSetting.new(:settings => mock("settings"), :desc => "a setting").group.should be_nil + end + end + + it "should be able to be converted into a resource" do + FileSetting.new(:settings => mock("settings"), :desc => "eh").should respond_to(:to_resource) + end + + describe "when being converted to a resource" do + before do + @settings = mock 'settings' + @file = Puppet::Util::Settings::FileSetting.new(:settings => @settings, :desc => "eh", :name => :mydir, :section => "mysect") + @settings.stubs(:value).with(:mydir).returns @basepath + end + + it "should skip files that cannot determine their types" do + @file.expects(:type).returns nil + @file.to_resource.should be_nil + end + + it "should skip non-existent files if 'create_files' is not enabled" do + @file.expects(:create_files?).returns false + @file.expects(:type).returns :file + File.expects(:exist?).with(@basepath).returns false + @file.to_resource.should be_nil + end + + it "should manage existent files even if 'create_files' is not enabled" do + @file.expects(:create_files?).returns false + @file.expects(:type).returns :file + File.expects(:exist?).with(@basepath).returns true + @file.to_resource.should be_instance_of(Puppet::Resource) + end + + describe "on POSIX systems" do + confine "no /dev on Win32" => Puppet.features.posix? + + it "should skip files in /dev" do + @settings.stubs(:value).with(:mydir).returns "/dev/file" + @file.to_resource.should be_nil + end + end + + it "should skip files whose paths are not strings" do + @settings.stubs(:value).with(:mydir).returns :foo + @file.to_resource.should be_nil + end + + it "should return a file resource with the path set appropriately" do + resource = @file.to_resource + resource.type.should == "File" + resource.title.should == @basepath + end + + it "should fully qualified returned files if necessary (#795)" do + @settings.stubs(:value).with(:mydir).returns "myfile" + @file.to_resource.title.should == File.join(Dir.getwd, "myfile") + end + + it "should set the mode on the file if a mode is provided" do + @file.mode = 0755 + + @file.to_resource[:mode].should == 0755 + end + + it "should not set the mode on a the file if manage_internal_file_permissions is disabled" do + Puppet[:manage_internal_file_permissions] = false + + @file.stubs(:mode).returns(0755) + + @file.to_resource[:mode].should == nil + end + + it "should set the owner if running as root and the owner is provided" do + Puppet.features.expects(:root?).returns true + @file.stubs(:owner).returns "foo" + @file.to_resource[:owner].should == "foo" + end + + it "should not set the owner if manage_internal_file_permissions is disabled" do + Puppet[:manage_internal_file_permissions] = false + Puppet.features.stubs(:root?).returns true + @file.stubs(:owner).returns "foo" + + @file.to_resource[:owner].should == nil + end + + it "should set the group if running as root and the group is provided" do + Puppet.features.expects(:root?).returns true + @file.stubs(:group).returns "foo" + @file.to_resource[:group].should == "foo" + end + + it "should not set the group if manage_internal_file_permissions is disabled" do + Puppet[:manage_internal_file_permissions] = false + Puppet.features.stubs(:root?).returns true + @file.stubs(:group).returns "foo" + + @file.to_resource[:group].should == nil + end + + + it "should not set owner if not running as root" do + Puppet.features.expects(:root?).returns false + @file.stubs(:owner).returns "foo" + @file.to_resource[:owner].should be_nil + end + + it "should not set group if not running as root" do + Puppet.features.expects(:root?).returns false + @file.stubs(:group).returns "foo" + @file.to_resource[:group].should be_nil + end + + it "should set :ensure to the file type" do + @file.expects(:type).returns :directory + @file.to_resource[:ensure].should == :directory + end + + it "should set the loglevel to :debug" do + @file.to_resource[:loglevel].should == :debug + end + + it "should set the backup to false" do + @file.to_resource[:backup].should be_false + end + + it "should tag the resource with the settings section" do + @file.expects(:section).returns "mysect" + @file.to_resource.should be_tagged("mysect") + end + + it "should tag the resource with the setting name" do + @file.to_resource.should be_tagged("mydir") + end + + it "should tag the resource with 'settings'" do + @file.to_resource.should be_tagged("settings") + end + end +end + diff --git a/spec/unit/util/settings/file_setting_spec_spec.rb b/spec/unit/util/settings/file_setting_spec_spec.rb deleted file mode 100755 index a6b7f024b..000000000 --- a/spec/unit/util/settings/file_setting_spec_spec.rb +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/util/settings' -require 'puppet/util/settings/file_setting' - -describe Puppet::Util::Settings::FileSetting do - FileSetting = Puppet::Util::Settings::FileSetting - - before do - @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" - end - - describe "when determining whether the service user should be used" do - before do - @settings = mock 'settings' - @settings.stubs(:[]).with(:mkusers).returns false - @settings.stubs(:service_user_available?).returns true - end - - it "should be true if the service user is available" do - @settings.expects(:service_user_available?).returns true - setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") - setting.should be_use_service_user - end - - it "should be true if 'mkusers' is set" do - @settings.expects(:[]).with(:mkusers).returns true - setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") - setting.should be_use_service_user - end - - it "should be false if the service user is not available and 'mkusers' is unset" do - setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") - setting.should be_use_service_user - end - end - - describe "when setting the owner" do - it "should allow the file to be owned by root" do - root_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "root", :desc => "a setting") } - root_owner.should_not raise_error - end - - it "should allow the file to be owned by the service user" do - service_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "service", :desc => "a setting") } - service_owner.should_not raise_error - end - - it "should allow the ownership of the file to be unspecified" do - no_owner = lambda { FileSetting.new(:settings => mock("settings"), :desc => "a setting") } - no_owner.should_not raise_error - end - - it "should not allow other owners" do - invalid_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "invalid", :desc => "a setting") } - invalid_owner.should raise_error(FileSetting::SettingError) - end - end - - describe "when reading the owner" do - it "should be root when the setting specifies root" do - setting = FileSetting.new(:settings => mock("settings"), :owner => "root", :desc => "a setting") - setting.owner.should == "root" - end - - it "should be the owner of the service when the setting specifies service and the service user should be used" do - settings = mock("settings") - settings.stubs(:[]).returns "the_service" - - setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") - setting.expects(:use_service_user?).returns true - setting.owner.should == "the_service" - end - - it "should be the root when the setting specifies service and the service user should not be used" do - settings = mock("settings") - settings.stubs(:[]).returns "the_service" - - setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") - setting.expects(:use_service_user?).returns false - setting.owner.should == "root" - end - - it "should be nil when the owner is unspecified" do - FileSetting.new(:settings => mock("settings"), :desc => "a setting").owner.should be_nil - end - end - - describe "when setting the group" do - it "should allow the group to be service" do - service_group = lambda { FileSetting.new(:settings => mock("settings"), :group => "service", :desc => "a setting") } - service_group.should_not raise_error - end - - it "should allow the group to be unspecified" do - no_group = lambda { FileSetting.new(:settings => mock("settings"), :desc => "a setting") } - no_group.should_not raise_error - end - - it "should not allow invalid groups" do - invalid_group = lambda { FileSetting.new(:settings => mock("settings"), :group => "invalid", :desc => "a setting") } - invalid_group.should raise_error(FileSetting::SettingError) - end - end - - describe "when reading the group" do - it "should be service when the setting specifies service" do - setting = FileSetting.new(:settings => mock("settings", :[] => "the_service"), :group => "service", :desc => "a setting") - setting.group.should == "the_service" - end - - it "should be nil when the group is unspecified" do - FileSetting.new(:settings => mock("settings"), :desc => "a setting").group.should be_nil - end - end - - it "should be able to be converted into a resource" do - FileSetting.new(:settings => mock("settings"), :desc => "eh").should respond_to(:to_resource) - end - - describe "when being converted to a resource" do - before do - @settings = mock 'settings' - @file = Puppet::Util::Settings::FileSetting.new(:settings => @settings, :desc => "eh", :name => :mydir, :section => "mysect") - @settings.stubs(:value).with(:mydir).returns @basepath - end - - it "should skip files that cannot determine their types" do - @file.expects(:type).returns nil - @file.to_resource.should be_nil - end - - it "should skip non-existent files if 'create_files' is not enabled" do - @file.expects(:create_files?).returns false - @file.expects(:type).returns :file - File.expects(:exist?).with(@basepath).returns false - @file.to_resource.should be_nil - end - - it "should manage existent files even if 'create_files' is not enabled" do - @file.expects(:create_files?).returns false - @file.expects(:type).returns :file - File.expects(:exist?).with(@basepath).returns true - @file.to_resource.should be_instance_of(Puppet::Resource) - end - - describe "on POSIX systems" do - confine "no /dev on Win32" => Puppet.features.posix? - - it "should skip files in /dev" do - @settings.stubs(:value).with(:mydir).returns "/dev/file" - @file.to_resource.should be_nil - end - end - - it "should skip files whose paths are not strings" do - @settings.stubs(:value).with(:mydir).returns :foo - @file.to_resource.should be_nil - end - - it "should return a file resource with the path set appropriately" do - resource = @file.to_resource - resource.type.should == "File" - resource.title.should == @basepath - end - - it "should fully qualified returned files if necessary (#795)" do - @settings.stubs(:value).with(:mydir).returns "myfile" - @file.to_resource.title.should == File.join(Dir.getwd, "myfile") - end - - it "should set the mode on the file if a mode is provided" do - @file.mode = 0755 - - @file.to_resource[:mode].should == 0755 - end - - it "should not set the mode on a the file if manage_internal_file_permissions is disabled" do - Puppet[:manage_internal_file_permissions] = false - - @file.stubs(:mode).returns(0755) - - @file.to_resource[:mode].should == nil - end - - it "should set the owner if running as root and the owner is provided" do - Puppet.features.expects(:root?).returns true - @file.stubs(:owner).returns "foo" - @file.to_resource[:owner].should == "foo" - end - - it "should not set the owner if manage_internal_file_permissions is disabled" do - Puppet[:manage_internal_file_permissions] = false - Puppet.features.stubs(:root?).returns true - @file.stubs(:owner).returns "foo" - - @file.to_resource[:owner].should == nil - end - - it "should set the group if running as root and the group is provided" do - Puppet.features.expects(:root?).returns true - @file.stubs(:group).returns "foo" - @file.to_resource[:group].should == "foo" - end - - it "should not set the group if manage_internal_file_permissions is disabled" do - Puppet[:manage_internal_file_permissions] = false - Puppet.features.stubs(:root?).returns true - @file.stubs(:group).returns "foo" - - @file.to_resource[:group].should == nil - end - - - it "should not set owner if not running as root" do - Puppet.features.expects(:root?).returns false - @file.stubs(:owner).returns "foo" - @file.to_resource[:owner].should be_nil - end - - it "should not set group if not running as root" do - Puppet.features.expects(:root?).returns false - @file.stubs(:group).returns "foo" - @file.to_resource[:group].should be_nil - end - - it "should set :ensure to the file type" do - @file.expects(:type).returns :directory - @file.to_resource[:ensure].should == :directory - end - - it "should set the loglevel to :debug" do - @file.to_resource[:loglevel].should == :debug - end - - it "should set the backup to false" do - @file.to_resource[:backup].should be_false - end - - it "should tag the resource with the settings section" do - @file.expects(:section).returns "mysect" - @file.to_resource.should be_tagged("mysect") - end - - it "should tag the resource with the setting name" do - @file.to_resource.should be_tagged("mydir") - end - - it "should tag the resource with 'settings'" do - @file.to_resource.should be_tagged("settings") - end - end -end - diff --git a/spec/unit/util/settings_spec.rb b/spec/unit/util/settings_spec.rb new file mode 100755 index 000000000..3e70cb581 --- /dev/null +++ b/spec/unit/util/settings_spec.rb @@ -0,0 +1,1111 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Util::Settings do + describe "when specifying defaults" do + before do + @settings = Puppet::Util::Settings.new + end + + it "should start with no defined parameters" do + @settings.params.length.should == 0 + end + + it "should allow specification of default values associated with a section as an array" do + @settings.setdefaults(:section, :myvalue => ["defaultval", "my description"]) + end + + it "should not allow duplicate parameter specifications" do + @settings.setdefaults(:section, :myvalue => ["a", "b"]) + lambda { @settings.setdefaults(:section, :myvalue => ["c", "d"]) }.should raise_error(ArgumentError) + end + + it "should allow specification of default values associated with a section as a hash" do + @settings.setdefaults(:section, :myvalue => {:default => "defaultval", :desc => "my description"}) + end + + it "should consider defined parameters to be valid" do + @settings.setdefaults(:section, :myvalue => ["defaultval", "my description"]) + @settings.valid?(:myvalue).should be_true + end + + it "should require a description when defaults are specified with an array" do + lambda { @settings.setdefaults(:section, :myvalue => ["a value"]) }.should raise_error(ArgumentError) + end + + it "should require a description when defaults are specified with a hash" do + lambda { @settings.setdefaults(:section, :myvalue => {:default => "a value"}) }.should raise_error(ArgumentError) + end + + it "should raise an error if we can't guess the type" do + lambda { @settings.setdefaults(:section, :myvalue => {:default => Object.new, :desc => "An impossible object"}) }.should raise_error(ArgumentError) + end + + it "should support specifying owner, group, and mode when specifying files" do + @settings.setdefaults(:section, :myvalue => {:default => "/some/file", :owner => "service", :mode => "boo", :group => "service", :desc => "whatever"}) + end + + it "should support specifying a short name" do + @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) + end + + it "should support specifying the setting type" do + @settings.setdefaults(:section, :myvalue => {:default => "/w", :desc => "b", :type => :setting}) + @settings.setting(:myvalue).should be_instance_of(Puppet::Util::Settings::Setting) + end + + it "should fail if an invalid setting type is specified" do + lambda { @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :type => :foo}) }.should raise_error(ArgumentError) + end + + it "should fail when short names conflict" do + @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) + lambda { @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) }.should raise_error(ArgumentError) + end + end + + describe "when setting values" do + before do + @settings = Puppet::Util::Settings.new + @settings.setdefaults :main, :myval => ["val", "desc"] + @settings.setdefaults :main, :bool => [true, "desc"] + end + + it "should provide a method for setting values from other objects" do + @settings[:myval] = "something else" + @settings[:myval].should == "something else" + end + + it "should support a getopt-specific mechanism for setting values" do + @settings.handlearg("--myval", "newval") + @settings[:myval].should == "newval" + end + + it "should support a getopt-specific mechanism for turning booleans off" do + @settings[:bool] = true + @settings.handlearg("--no-bool", "") + @settings[:bool].should == false + end + + it "should support a getopt-specific mechanism for turning booleans on" do + # Turn it off first + @settings[:bool] = false + @settings.handlearg("--bool", "") + @settings[:bool].should == true + end + + it "should consider a cli setting with no argument to be a boolean" do + # Turn it off first + @settings[:bool] = false + @settings.handlearg("--bool") + @settings[:bool].should == true + end + + it "should consider a cli setting with an empty string as an argument to be a boolean, if the setting itself is a boolean" do + # Turn it off first + @settings[:bool] = false + @settings.handlearg("--bool", "") + @settings[:bool].should == true + end + + it "should consider a cli setting with an empty string as an argument to be an empty argument, if the setting itself is not a boolean" do + @settings[:myval] = "bob" + @settings.handlearg("--myval", "") + @settings[:myval].should == "" + end + + it "should consider a cli setting with a boolean as an argument to be a boolean" do + # Turn it off first + @settings[:bool] = false + @settings.handlearg("--bool", "true") + @settings[:bool].should == true + end + + it "should not consider a cli setting of a non boolean with a boolean as an argument to be a boolean" do + # Turn it off first + @settings[:myval] = "bob" + @settings.handlearg("--no-myval", "") + @settings[:myval].should == "" + end + + it "should clear the cache when setting getopt-specific values" do + @settings.setdefaults :mysection, :one => ["whah", "yay"], :two => ["$one yay", "bah"] + @settings[:two].should == "whah yay" + @settings.handlearg("--one", "else") + @settings[:two].should == "else yay" + end + + it "should not clear other values when setting getopt-specific values" do + @settings[:myval] = "yay" + @settings.handlearg("--no-bool", "") + @settings[:myval].should == "yay" + end + + it "should clear the list of used sections" do + @settings.expects(:clearused) + @settings[:myval] = "yay" + end + + it "should call passed blocks when values are set" do + values = [] + @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) + values.should == [] + + @settings[:hooker] = "something" + values.should == %w{something} + end + + it "should call passed blocks when values are set via the command line" do + values = [] + @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) + values.should == [] + + @settings.handlearg("--hooker", "yay") + + values.should == %w{yay} + end + + it "should provide an option to call passed blocks during definition" do + values = [] + @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :call_on_define => true, :hook => lambda { |v| values << v }}) + values.should == %w{yay} + end + + it "should pass the fully interpolated value to the hook when called on definition" do + values = [] + @settings.setdefaults(:section, :one => ["test", "a"]) + @settings.setdefaults(:section, :hooker => {:default => "$one/yay", :desc => "boo", :call_on_define => true, :hook => lambda { |v| values << v }}) + values.should == %w{test/yay} + end + + it "should munge values using the setting-specific methods" do + @settings[:bool] = "false" + @settings[:bool].should == false + end + + it "should prefer cli values to values set in Ruby code" do + @settings.handlearg("--myval", "cliarg") + @settings[:myval] = "memarg" + @settings[:myval].should == "cliarg" + end + + it "should clear the list of environments" do + Puppet::Node::Environment.expects(:clear).at_least(1) + @settings[:myval] = "memarg" + end + + it "should raise an error if we try to set 'name'" do + lambda{ @settings[:name] = "foo" }.should raise_error(ArgumentError) + end + + it "should raise an error if we try to set 'mode'" do + lambda{ @settings[:mode] = "foo" }.should raise_error(ArgumentError) + end + + it "should warn and use [master] if we ask for [puppetmasterd]" do + Puppet.expects(:warning) + @settings.set_value(:myval, "foo", :puppetmasterd) + + @settings.stubs(:mode).returns(:master) + @settings.value(:myval).should == "foo" + end + + it "should warn and use [agent] if we ask for [puppetd]" do + Puppet.expects(:warning) + @settings.set_value(:myval, "foo", :puppetd) + + @settings.stubs(:mode).returns(:agent) + @settings.value(:myval).should == "foo" + end + end + + describe "when returning values" do + before do + @settings = Puppet::Util::Settings.new + @settings.setdefaults :section, :config => ["/my/file", "eh"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"], :four => ["$two $three FOUR", "d"] + FileTest.stubs(:exist?).returns true + end + + it "should provide a mechanism for returning set values" do + @settings[:one] = "other" + @settings[:one].should == "other" + end + + it "should interpolate default values for other parameters into returned parameter values" do + @settings[:one].should == "ONE" + @settings[:two].should == "ONE TWO" + @settings[:three].should == "ONE ONE TWO THREE" + end + + it "should interpolate default values that themselves need to be interpolated" do + @settings[:four].should == "ONE TWO ONE ONE TWO THREE FOUR" + end + + it "should provide a method for returning uninterpolated values" do + @settings[:two] = "$one tw0" + @settings.uninterpolated_value(:two).should == "$one tw0" + @settings.uninterpolated_value(:four).should == "$two $three FOUR" + end + + it "should interpolate set values for other parameters into returned parameter values" do + @settings[:one] = "on3" + @settings[:two] = "$one tw0" + @settings[:three] = "$one $two thr33" + @settings[:four] = "$one $two $three f0ur" + @settings[:one].should == "on3" + @settings[:two].should == "on3 tw0" + @settings[:three].should == "on3 on3 tw0 thr33" + @settings[:four].should == "on3 on3 tw0 on3 on3 tw0 thr33 f0ur" + end + + it "should not cache interpolated values such that stale information is returned" do + @settings[:two].should == "ONE TWO" + @settings[:one] = "one" + @settings[:two].should == "one TWO" + end + + it "should not cache values such that information from one environment is returned for another environment" do + text = "[env1]\none = oneval\n[env2]\none = twoval\n" + @settings.stubs(:read_file).returns(text) + @settings.parse + + @settings.value(:one, "env1").should == "oneval" + @settings.value(:one, "env2").should == "twoval" + end + + it "should have a mode determined by the 'mode' parameter that cannot be edited" do + @settings.setdefaults(:whatever, :mode => ["something", "yayness"]) + @settings.mode.should == :something + + lambda{ @settings[:mode] = :other }.should raise_error + end + end + + describe "when choosing which value to return" do + before do + @settings = Puppet::Util::Settings.new + @settings.setdefaults :section, + :config => ["/my/file", "a"], + :one => ["ONE", "a"], + :mode => ["mymode", "w"] + FileTest.stubs(:exist?).returns true + end + + it "should return default values if no values have been set" do + @settings[:one].should == "ONE" + end + + it "should return values set on the cli before values set in the configuration file" do + text = "[main]\none = fileval\n" + @settings.stubs(:read_file).returns(text) + @settings.handlearg("--one", "clival") + @settings.parse + + @settings[:one].should == "clival" + end + + it "should return values set on the cli before values set in Ruby" do + @settings[:one] = "rubyval" + @settings.handlearg("--one", "clival") + @settings[:one].should == "clival" + end + + it "should return values set in the mode-specific section before values set in the main section" do + text = "[main]\none = mainval\n[mymode]\none = modeval\n" + @settings.stubs(:read_file).returns(text) + @settings.parse + + @settings[:one].should == "modeval" + end + + it "should not return values outside of its search path" do + text = "[other]\none = oval\n" + file = "/some/file" + @settings.stubs(:read_file).returns(text) + @settings.parse + @settings[:one].should == "ONE" + end + + it "should return values in a specified environment" do + text = "[env]\none = envval\n" + @settings.stubs(:read_file).returns(text) + @settings.parse + @settings.value(:one, "env").should == "envval" + end + + it "should interpolate found values using the current environment" do + @settings.setdefaults :main, :myval => ["$environment/foo", "mydocs"] + + @settings.value(:myval, "myenv").should == "myenv/foo" + end + + it "should return values in a specified environment before values in the main or name sections" do + text = "[env]\none = envval\n[main]\none = mainval\n[myname]\none = nameval\n" + @settings.stubs(:read_file).returns(text) + @settings.parse + @settings.value(:one, "env").should == "envval" + end + end + + describe "when parsing its configuration" do + before do + @settings = Puppet::Util::Settings.new + @settings.stubs(:service_user_available?).returns true + @file = "/some/file" + @settings.setdefaults :section, :user => ["suser", "doc"], :group => ["sgroup", "doc"] + @settings.setdefaults :section, :config => ["/some/file", "eh"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"] + FileTest.stubs(:exist?).returns true + end + + it "should use its current ':config' value for the file to parse" do + myfile = Puppet.features.posix? ? "/my/file" : "C:/myfile" # do not stub expand_path here, as this leads to a stack overflow, when mocha tries to use it + @settings[:config] = myfile + + File.expects(:read).with(myfile).returns "[main]" + + @settings.parse + end + + it "should fail if no configuration setting is defined" do + @settings = Puppet::Util::Settings.new + lambda { @settings.parse }.should raise_error(RuntimeError) + end + + it "should not try to parse non-existent files" do + FileTest.expects(:exist?).with("/some/file").returns false + + File.expects(:read).with("/some/file").never + + @settings.parse + end + + it "should set a timer that triggers reparsing, even if the file does not exist" do + FileTest.expects(:exist?).returns false + @settings.expects(:set_filetimeout_timer) + + @settings.parse + end + + it "should return values set in the configuration file" do + text = "[main] + one = fileval + " + @settings.expects(:read_file).returns(text) + @settings.parse + @settings[:one].should == "fileval" + end + + #484 - this should probably be in the regression area + it "should not throw an exception on unknown parameters" do + text = "[main]\nnosuchparam = mval\n" + @settings.expects(:read_file).returns(text) + lambda { @settings.parse }.should_not raise_error + end + + it "should convert booleans in the configuration file into Ruby booleans" do + text = "[main] + one = true + two = false + " + @settings.expects(:read_file).returns(text) + @settings.parse + @settings[:one].should == true + @settings[:two].should == false + end + + it "should convert integers in the configuration file into Ruby Integers" do + text = "[main] + one = 65 + " + @settings.expects(:read_file).returns(text) + @settings.parse + @settings[:one].should == 65 + end + + it "should support specifying all metadata (owner, group, mode) in the configuration file" do + @settings.setdefaults :section, :myfile => ["/myfile", "a"] + + text = "[main] + myfile = /other/file {owner = service, group = service, mode = 644} + " + @settings.expects(:read_file).returns(text) + @settings.parse + @settings[:myfile].should == "/other/file" + @settings.metadata(:myfile).should == {:owner => "suser", :group => "sgroup", :mode => "644"} + end + + it "should support specifying a single piece of metadata (owner, group, or mode) in the configuration file" do + @settings.setdefaults :section, :myfile => ["/myfile", "a"] + + text = "[main] + myfile = /other/file {owner = service} + " + file = "/some/file" + @settings.expects(:read_file).returns(text) + @settings.parse + @settings[:myfile].should == "/other/file" + @settings.metadata(:myfile).should == {:owner => "suser"} + end + + it "should call hooks associated with values set in the configuration file" do + values = [] + @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} + + text = "[main] + mysetting = setval + " + @settings.expects(:read_file).returns(text) + @settings.parse + values.should == ["setval"] + end + + it "should not call the same hook for values set multiple times in the configuration file" do + values = [] + @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} + + text = "[main] + mysetting = setval + [puppet] + mysetting = other + " + @settings.expects(:read_file).returns(text) + @settings.parse + values.should == ["setval"] + end + + it "should pass the environment-specific value to the hook when one is available" do + values = [] + @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} + @settings.setdefaults :section, :environment => ["yay", "a"] + @settings.setdefaults :section, :environments => ["yay,foo", "a"] + + text = "[main] + mysetting = setval + [yay] + mysetting = other + " + @settings.expects(:read_file).returns(text) + @settings.parse + values.should == ["other"] + end + + it "should pass the interpolated value to the hook when one is available" do + values = [] + @settings.setdefaults :section, :base => {:default => "yay", :desc => "a", :hook => proc { |v| values << v }} + @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} + + text = "[main] + mysetting = $base/setval + " + @settings.expects(:read_file).returns(text) + @settings.parse + values.should == ["yay/setval"] + end + + it "should allow empty values" do + @settings.setdefaults :section, :myarg => ["myfile", "a"] + + text = "[main] + myarg = + " + @settings.stubs(:read_file).returns(text) + @settings.parse + @settings[:myarg].should == "" + end + + describe "and when reading a non-positive filetimeout value from the config file" do + before do + @settings.setdefaults :foo, :filetimeout => [5, "eh"] + + somefile = "/some/file" + text = "[main] + filetimeout = -1 + " + File.expects(:read).with(somefile).returns(text) + File.expects(:expand_path).with(somefile).returns somefile + @settings[:config] = somefile + end + + it "should not set a timer" do + EventLoop::Timer.expects(:new).never + + @settings.parse + end + end + end + + describe "when reparsing its configuration" do + before do + @settings = Puppet::Util::Settings.new + @settings.setdefaults :section, :config => ["/test/file", "a"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"] + FileTest.stubs(:exist?).returns true + end + + it "should use a LoadedFile instance to determine if the file has changed" do + file = mock 'file' + Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file + + file.expects(:changed?) + + @settings.stubs(:parse) + @settings.reparse + end + + it "should not create the LoadedFile instance and should not parse if the file does not exist" do + FileTest.expects(:exist?).with("/test/file").returns false + Puppet::Util::LoadedFile.expects(:new).never + + @settings.expects(:parse).never + + @settings.reparse + end + + it "should not reparse if the file has not changed" do + file = mock 'file' + Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file + + file.expects(:changed?).returns false + + @settings.expects(:parse).never + + @settings.reparse + end + + it "should reparse if the file has changed" do + file = stub 'file', :file => "/test/file" + Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file + + file.expects(:changed?).returns true + + @settings.expects(:parse) + + @settings.reparse + end + + it "should use a cached LoadedFile instance" do + first = mock 'first' + second = mock 'second' + Puppet::Util::LoadedFile.expects(:new).times(2).with("/test/file").returns(first).then.returns(second) + + @settings.file.should equal(first) + Puppet::Util::Cacher.expire + @settings.file.should equal(second) + end + + it "should replace in-memory values with on-file values" do + # Init the value + text = "[main]\none = disk-init\n" + file = mock 'file' + file.stubs(:changed?).returns(true) + file.stubs(:file).returns("/test/file") + @settings[:one] = "init" + @settings.file = file + + # Now replace the value + text = "[main]\none = disk-replace\n" + + # This is kinda ridiculous - the reason it parses twice is that + # it goes to parse again when we ask for the value, because the + # mock always says it should get reparsed. + @settings.stubs(:read_file).returns(text) + @settings.reparse + @settings[:one].should == "disk-replace" + end + + it "should retain parameters set by cli when configuration files are reparsed" do + @settings.handlearg("--one", "clival") + + text = "[main]\none = on-disk\n" + @settings.stubs(:read_file).returns(text) + @settings.parse + + @settings[:one].should == "clival" + end + + it "should remove in-memory values that are no longer set in the file" do + # Init the value + text = "[main]\none = disk-init\n" + @settings.expects(:read_file).returns(text) + @settings.parse + @settings[:one].should == "disk-init" + + # Now replace the value + text = "[main]\ntwo = disk-replace\n" + @settings.expects(:read_file).returns(text) + @settings.parse + #@settings.reparse + + # The originally-overridden value should be replaced with the default + @settings[:one].should == "ONE" + + # and we should now have the new value in memory + @settings[:two].should == "disk-replace" + end + + it "should retain in-memory values if the file has a syntax error" do + # Init the value + text = "[main]\none = initial-value\n" + @settings.expects(:read_file).returns(text) + @settings.parse + @settings[:one].should == "initial-value" + + # Now replace the value with something bogus + text = "[main]\nkenny = killed-by-what-follows\n1 is 2, blah blah florp\n" + @settings.expects(:read_file).returns(text) + @settings.parse + + # The originally-overridden value should not be replaced with the default + @settings[:one].should == "initial-value" + + # and we should not have the new value in memory + @settings[:kenny].should be_nil + end + end + + it "should provide a method for creating a catalog of resources from its configuration" do + Puppet::Util::Settings.new.should respond_to(:to_catalog) + end + + describe "when creating a catalog" do + before do + @settings = Puppet::Util::Settings.new + @settings.stubs(:service_user_available?).returns true + @prefix = Puppet.features.posix? ? "" : "C:" + end + + it "should add all file resources to the catalog if no sections have been specified" do + @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"], :seconddir => [@prefix+"/seconddir", "a"] + @settings.setdefaults :other, :otherdir => [@prefix+"/otherdir", "a"] + + catalog = @settings.to_catalog + + [@prefix+"/maindir", @prefix+"/seconddir", @prefix+"/otherdir"].each do |path| + catalog.resource(:file, path).should be_instance_of(Puppet::Resource) + end + end + + it "should add only files in the specified sections if section names are provided" do + @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] + @settings.setdefaults :other, :otherdir => [@prefix+"/otherdir", "a"] + catalog = @settings.to_catalog(:main) + catalog.resource(:file, @prefix+"/otherdir").should be_nil + catalog.resource(:file, @prefix+"/maindir").should be_instance_of(Puppet::Resource) + end + + it "should not try to add the same file twice" do + @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] + @settings.setdefaults :other, :otherdir => [@prefix+"/maindir", "a"] + lambda { @settings.to_catalog }.should_not raise_error + end + + it "should ignore files whose :to_resource method returns nil" do + @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] + @settings.setting(:maindir).expects(:to_resource).returns nil + + Puppet::Resource::Catalog.any_instance.expects(:add_resource).never + @settings.to_catalog + end + + describe "when adding users and groups to the catalog" do + before do + Puppet.features.stubs(:root?).returns true + @settings.setdefaults :foo, :mkusers => [true, "e"], :user => ["suser", "doc"], :group => ["sgroup", "doc"] + @settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} + + @catalog = @settings.to_catalog + end + + it "should add each specified user and group to the catalog if :mkusers is a valid setting, is enabled, and we're running as root" do + @catalog.resource(:user, "suser").should be_instance_of(Puppet::Resource) + @catalog.resource(:group, "sgroup").should be_instance_of(Puppet::Resource) + end + + it "should only add users and groups to the catalog from specified sections" do + @settings.setdefaults :yay, :yaydir => {:default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} + catalog = @settings.to_catalog(:other) + catalog.resource(:user, "jane").should be_nil + catalog.resource(:group, "billy").should be_nil + end + + it "should not add users or groups to the catalog if :mkusers not running as root" do + Puppet.features.stubs(:root?).returns false + + catalog = @settings.to_catalog + catalog.resource(:user, "suser").should be_nil + catalog.resource(:group, "sgroup").should be_nil + end + + it "should not add users or groups to the catalog if :mkusers is not a valid setting" do + Puppet.features.stubs(:root?).returns true + settings = Puppet::Util::Settings.new + settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} + + catalog = settings.to_catalog + catalog.resource(:user, "suser").should be_nil + catalog.resource(:group, "sgroup").should be_nil + end + + it "should not add users or groups to the catalog if :mkusers is a valid setting but is disabled" do + @settings[:mkusers] = false + + catalog = @settings.to_catalog + catalog.resource(:user, "suser").should be_nil + catalog.resource(:group, "sgroup").should be_nil + end + + it "should not try to add users or groups to the catalog twice" do + @settings.setdefaults :yay, :yaydir => {:default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} + + # This would fail if users/groups were added twice + lambda { @settings.to_catalog }.should_not raise_error + end + + it "should set :ensure to :present on each created user and group" do + @catalog.resource(:user, "suser")[:ensure].should == :present + @catalog.resource(:group, "sgroup")[:ensure].should == :present + end + + it "should set each created user's :gid to the service group" do + @settings.to_catalog.resource(:user, "suser")[:gid].should == "sgroup" + end + + it "should not attempt to manage the root user" do + Puppet.features.stubs(:root?).returns true + @settings.setdefaults :foo, :foodir => {:default => "/foodir", :desc => "a", :owner => "root", :group => "service"} + + @settings.to_catalog.resource(:user, "root").should be_nil + end + end + end + + it "should be able to be converted to a manifest" do + Puppet::Util::Settings.new.should respond_to(:to_manifest) + end + + describe "when being converted to a manifest" do + it "should produce a string with the code for each resource joined by two carriage returns" do + @settings = Puppet::Util::Settings.new + @settings.setdefaults :main, :maindir => ["/maindir", "a"], :seconddir => ["/seconddir", "a"] + + main = stub 'main_resource', :ref => "File[/maindir]" + main.expects(:to_manifest).returns "maindir" + second = stub 'second_resource', :ref => "File[/seconddir]" + second.expects(:to_manifest).returns "seconddir" + @settings.setting(:maindir).expects(:to_resource).returns main + @settings.setting(:seconddir).expects(:to_resource).returns second + + @settings.to_manifest.split("\n\n").sort.should == %w{maindir seconddir} + end + end + + describe "when using sections of the configuration to manage the local host" do + before do + @settings = Puppet::Util::Settings.new + @settings.stubs(:service_user_available?).returns true + @settings.setdefaults :main, :noop => [false, ""] + @settings.setdefaults :main, :maindir => ["/maindir", "a"], :seconddir => ["/seconddir", "a"] + @settings.setdefaults :main, :user => ["suser", "doc"], :group => ["sgroup", "doc"] + @settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service", :mode => 0755} + @settings.setdefaults :third, :thirddir => ["/thirddir", "b"] + @settings.setdefaults :files, :myfile => {:default => "/myfile", :desc => "a", :mode => 0755} + end + + it "should provide a method that writes files with the correct modes" do + @settings.should respond_to(:write) + end + + it "should provide a method that creates directories with the correct modes" do + Puppet::Util::SUIDManager.expects(:asuser).with("suser", "sgroup").yields + Dir.expects(:mkdir).with("/otherdir", 0755) + @settings.mkdir(:otherdir) + end + + it "should create a catalog with the specified sections" do + @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") + @settings.use(:main, :other) + end + + it "should canonicalize the sections" do + @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") + @settings.use("main", "other") + end + + it "should ignore sections that have already been used" do + @settings.expects(:to_catalog).with(:main).returns Puppet::Resource::Catalog.new("foo") + @settings.use(:main) + @settings.expects(:to_catalog).with(:other).returns Puppet::Resource::Catalog.new("foo") + @settings.use(:main, :other) + end + + it "should ignore tags and schedules when creating files and directories" + + it "should be able to provide all of its parameters in a format compatible with GetOpt::Long" do + pending "Not converted from test/unit yet" + end + + it "should convert the created catalog to a RAL catalog" do + @catalog = Puppet::Resource::Catalog.new("foo") + @settings.expects(:to_catalog).with(:main).returns @catalog + + @catalog.expects(:to_ral).returns @catalog + @settings.use(:main) + end + + it "should specify that it is not managing a host catalog" do + catalog = Puppet::Resource::Catalog.new("foo") + @settings.expects(:to_catalog).returns catalog + + catalog.stubs(:to_ral).returns catalog + + catalog.expects(:host_config=).with false + + @settings.use(:main) + end + + it "should support a method for re-using all currently used sections" do + @settings.expects(:to_catalog).with(:main, :third).times(2).returns Puppet::Resource::Catalog.new("foo") + + @settings.use(:main, :third) + @settings.reuse + end + + it "should fail with an appropriate message if any resources fail" do + @catalog = Puppet::Resource::Catalog.new("foo") + @catalog.stubs(:to_ral).returns @catalog + @settings.expects(:to_catalog).returns @catalog + + @trans = mock("transaction") + @catalog.expects(:apply).yields(@trans) + + @trans.expects(:any_failed?).returns(true) + + report = mock 'report' + @trans.expects(:report).returns report + + log = mock 'log', :to_s => "My failure", :level => :err + report.expects(:logs).returns [log] + + @settings.expects(:raise).with { |msg| msg.include?("My failure") } + @settings.use(:whatever) + end + end + + describe "when dealing with printing configs" do + before do + @settings = Puppet::Util::Settings.new + #these are the magic default values + @settings.stubs(:value).with(:configprint).returns("") + @settings.stubs(:value).with(:genconfig).returns(false) + @settings.stubs(:value).with(:genmanifest).returns(false) + @settings.stubs(:value).with(:environment).returns(nil) + end + + describe "when checking print_config?" do + it "should return false when the :configprint, :genconfig and :genmanifest are not set" do + @settings.print_configs?.should be_false + end + + it "should return true when :configprint has a value" do + @settings.stubs(:value).with(:configprint).returns("something") + @settings.print_configs?.should be_true + end + + it "should return true when :genconfig has a value" do + @settings.stubs(:value).with(:genconfig).returns(true) + @settings.print_configs?.should be_true + end + + it "should return true when :genmanifest has a value" do + @settings.stubs(:value).with(:genmanifest).returns(true) + @settings.print_configs?.should be_true + end + end + + describe "when printing configs" do + describe "when :configprint has a value" do + it "should call print_config_options" do + @settings.stubs(:value).with(:configprint).returns("something") + @settings.expects(:print_config_options) + @settings.print_configs + end + + it "should get the value of the option using the environment" do + @settings.stubs(:value).with(:configprint).returns("something") + @settings.stubs(:include?).with("something").returns(true) + @settings.expects(:value).with(:environment).returns("env") + @settings.expects(:value).with("something", "env").returns("foo") + @settings.stubs(:puts).with("foo") + @settings.print_configs + end + + it "should print the value of the option" do + @settings.stubs(:value).with(:configprint).returns("something") + @settings.stubs(:include?).with("something").returns(true) + @settings.stubs(:value).with("something", nil).returns("foo") + @settings.expects(:puts).with("foo") + @settings.print_configs + end + + it "should print the value pairs if there are multiple options" do + @settings.stubs(:value).with(:configprint).returns("bar,baz") + @settings.stubs(:include?).with("bar").returns(true) + @settings.stubs(:include?).with("baz").returns(true) + @settings.stubs(:value).with("bar", nil).returns("foo") + @settings.stubs(:value).with("baz", nil).returns("fud") + @settings.expects(:puts).with("bar = foo") + @settings.expects(:puts).with("baz = fud") + @settings.print_configs + end + + it "should print a whole bunch of stuff if :configprint = all" + + it "should return true after printing" do + @settings.stubs(:value).with(:configprint).returns("something") + @settings.stubs(:include?).with("something").returns(true) + @settings.stubs(:value).with("something", nil).returns("foo") + @settings.stubs(:puts).with("foo") + @settings.print_configs.should be_true + end + + it "should return false if a config param is not found" do + @settings.stubs :puts + @settings.stubs(:value).with(:configprint).returns("something") + @settings.stubs(:include?).with("something").returns(false) + @settings.print_configs.should be_false + end + end + + describe "when genconfig is true" do + before do + @settings.stubs :puts + end + + it "should call to_config" do + @settings.stubs(:value).with(:genconfig).returns(true) + @settings.expects(:to_config) + @settings.print_configs + end + + it "should return true from print_configs" do + @settings.stubs(:value).with(:genconfig).returns(true) + @settings.stubs(:to_config) + @settings.print_configs.should be_true + end + end + + describe "when genmanifest is true" do + before do + @settings.stubs :puts + end + + it "should call to_config" do + @settings.stubs(:value).with(:genmanifest).returns(true) + @settings.expects(:to_manifest) + @settings.print_configs + end + + it "should return true from print_configs" do + @settings.stubs(:value).with(:genmanifest).returns(true) + @settings.stubs(:to_manifest) + @settings.print_configs.should be_true + end + end + end + end + + describe "when setting a timer to trigger configuration file reparsing" do + before do + @settings = Puppet::Util::Settings.new + @settings.setdefaults :foo, :filetimeout => [5, "eh"] + end + + it "should do nothing if no filetimeout setting is available" do + @settings.expects(:value).with(:filetimeout).returns nil + EventLoop::Timer.expects(:new).never + @settings.set_filetimeout_timer + end + + it "should always convert the timer interval to an integer" do + @settings.expects(:value).with(:filetimeout).returns "10" + EventLoop::Timer.expects(:new).with(:interval => 10, :start? => true, :tolerance => 1) + @settings.set_filetimeout_timer + end + + it "should do nothing if the filetimeout setting is not greater than 0" do + @settings.expects(:value).with(:filetimeout).returns -2 + EventLoop::Timer.expects(:new).never + @settings.set_filetimeout_timer + end + + it "should create a timer with its interval set to the filetimeout, start? set to true, and a tolerance of 1" do + @settings.expects(:value).with(:filetimeout).returns 5 + EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1) + + @settings.set_filetimeout_timer + end + + it "should reparse when the timer goes off" do + EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1).yields + + @settings.expects(:reparse) + + @settings.set_filetimeout_timer + end + end + + describe "when determining if the service user is available" do + it "should return false if there is no user setting" do + Puppet::Util::Settings.new.should_not be_service_user_available + end + + it "should return false if the user provider says the user is missing" do + settings = Puppet::Util::Settings.new + settings.setdefaults :main, :user => ["foo", "doc"] + + user = mock 'user' + user.expects(:exists?).returns false + + Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == "foo" }.returns user + + settings.should_not be_service_user_available + end + + it "should return true if the user provider says the user is present" do + settings = Puppet::Util::Settings.new + settings.setdefaults :main, :user => ["foo", "doc"] + + user = mock 'user' + user.expects(:exists?).returns true + + Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == "foo" }.returns user + + settings.should be_service_user_available + end + + it "should cache the result" + end + + describe "#without_noop" do + before do + @settings = Puppet::Util::Settings.new + @settings.setdefaults :main, :noop => [true, ""] + end + + it "should set noop to false for the duration of the block" do + @settings.without_noop { @settings.value(:noop, :cli).should be_false } + end + + it "should ensure that noop is returned to its previous value" do + @settings.without_noop { raise } rescue nil + @settings.value(:noop, :cli).should be_true + end + + it "should work even if no 'noop' setting is available" do + settings = Puppet::Util::Settings.new + stuff = nil + settings.without_noop { stuff = "yay" } + stuff.should == "yay" + end + end +end diff --git a/spec/unit/util/settings_spec_spec.rb b/spec/unit/util/settings_spec_spec.rb deleted file mode 100755 index 3e70cb581..000000000 --- a/spec/unit/util/settings_spec_spec.rb +++ /dev/null @@ -1,1111 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Util::Settings do - describe "when specifying defaults" do - before do - @settings = Puppet::Util::Settings.new - end - - it "should start with no defined parameters" do - @settings.params.length.should == 0 - end - - it "should allow specification of default values associated with a section as an array" do - @settings.setdefaults(:section, :myvalue => ["defaultval", "my description"]) - end - - it "should not allow duplicate parameter specifications" do - @settings.setdefaults(:section, :myvalue => ["a", "b"]) - lambda { @settings.setdefaults(:section, :myvalue => ["c", "d"]) }.should raise_error(ArgumentError) - end - - it "should allow specification of default values associated with a section as a hash" do - @settings.setdefaults(:section, :myvalue => {:default => "defaultval", :desc => "my description"}) - end - - it "should consider defined parameters to be valid" do - @settings.setdefaults(:section, :myvalue => ["defaultval", "my description"]) - @settings.valid?(:myvalue).should be_true - end - - it "should require a description when defaults are specified with an array" do - lambda { @settings.setdefaults(:section, :myvalue => ["a value"]) }.should raise_error(ArgumentError) - end - - it "should require a description when defaults are specified with a hash" do - lambda { @settings.setdefaults(:section, :myvalue => {:default => "a value"}) }.should raise_error(ArgumentError) - end - - it "should raise an error if we can't guess the type" do - lambda { @settings.setdefaults(:section, :myvalue => {:default => Object.new, :desc => "An impossible object"}) }.should raise_error(ArgumentError) - end - - it "should support specifying owner, group, and mode when specifying files" do - @settings.setdefaults(:section, :myvalue => {:default => "/some/file", :owner => "service", :mode => "boo", :group => "service", :desc => "whatever"}) - end - - it "should support specifying a short name" do - @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) - end - - it "should support specifying the setting type" do - @settings.setdefaults(:section, :myvalue => {:default => "/w", :desc => "b", :type => :setting}) - @settings.setting(:myvalue).should be_instance_of(Puppet::Util::Settings::Setting) - end - - it "should fail if an invalid setting type is specified" do - lambda { @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :type => :foo}) }.should raise_error(ArgumentError) - end - - it "should fail when short names conflict" do - @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) - lambda { @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) }.should raise_error(ArgumentError) - end - end - - describe "when setting values" do - before do - @settings = Puppet::Util::Settings.new - @settings.setdefaults :main, :myval => ["val", "desc"] - @settings.setdefaults :main, :bool => [true, "desc"] - end - - it "should provide a method for setting values from other objects" do - @settings[:myval] = "something else" - @settings[:myval].should == "something else" - end - - it "should support a getopt-specific mechanism for setting values" do - @settings.handlearg("--myval", "newval") - @settings[:myval].should == "newval" - end - - it "should support a getopt-specific mechanism for turning booleans off" do - @settings[:bool] = true - @settings.handlearg("--no-bool", "") - @settings[:bool].should == false - end - - it "should support a getopt-specific mechanism for turning booleans on" do - # Turn it off first - @settings[:bool] = false - @settings.handlearg("--bool", "") - @settings[:bool].should == true - end - - it "should consider a cli setting with no argument to be a boolean" do - # Turn it off first - @settings[:bool] = false - @settings.handlearg("--bool") - @settings[:bool].should == true - end - - it "should consider a cli setting with an empty string as an argument to be a boolean, if the setting itself is a boolean" do - # Turn it off first - @settings[:bool] = false - @settings.handlearg("--bool", "") - @settings[:bool].should == true - end - - it "should consider a cli setting with an empty string as an argument to be an empty argument, if the setting itself is not a boolean" do - @settings[:myval] = "bob" - @settings.handlearg("--myval", "") - @settings[:myval].should == "" - end - - it "should consider a cli setting with a boolean as an argument to be a boolean" do - # Turn it off first - @settings[:bool] = false - @settings.handlearg("--bool", "true") - @settings[:bool].should == true - end - - it "should not consider a cli setting of a non boolean with a boolean as an argument to be a boolean" do - # Turn it off first - @settings[:myval] = "bob" - @settings.handlearg("--no-myval", "") - @settings[:myval].should == "" - end - - it "should clear the cache when setting getopt-specific values" do - @settings.setdefaults :mysection, :one => ["whah", "yay"], :two => ["$one yay", "bah"] - @settings[:two].should == "whah yay" - @settings.handlearg("--one", "else") - @settings[:two].should == "else yay" - end - - it "should not clear other values when setting getopt-specific values" do - @settings[:myval] = "yay" - @settings.handlearg("--no-bool", "") - @settings[:myval].should == "yay" - end - - it "should clear the list of used sections" do - @settings.expects(:clearused) - @settings[:myval] = "yay" - end - - it "should call passed blocks when values are set" do - values = [] - @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) - values.should == [] - - @settings[:hooker] = "something" - values.should == %w{something} - end - - it "should call passed blocks when values are set via the command line" do - values = [] - @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) - values.should == [] - - @settings.handlearg("--hooker", "yay") - - values.should == %w{yay} - end - - it "should provide an option to call passed blocks during definition" do - values = [] - @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :call_on_define => true, :hook => lambda { |v| values << v }}) - values.should == %w{yay} - end - - it "should pass the fully interpolated value to the hook when called on definition" do - values = [] - @settings.setdefaults(:section, :one => ["test", "a"]) - @settings.setdefaults(:section, :hooker => {:default => "$one/yay", :desc => "boo", :call_on_define => true, :hook => lambda { |v| values << v }}) - values.should == %w{test/yay} - end - - it "should munge values using the setting-specific methods" do - @settings[:bool] = "false" - @settings[:bool].should == false - end - - it "should prefer cli values to values set in Ruby code" do - @settings.handlearg("--myval", "cliarg") - @settings[:myval] = "memarg" - @settings[:myval].should == "cliarg" - end - - it "should clear the list of environments" do - Puppet::Node::Environment.expects(:clear).at_least(1) - @settings[:myval] = "memarg" - end - - it "should raise an error if we try to set 'name'" do - lambda{ @settings[:name] = "foo" }.should raise_error(ArgumentError) - end - - it "should raise an error if we try to set 'mode'" do - lambda{ @settings[:mode] = "foo" }.should raise_error(ArgumentError) - end - - it "should warn and use [master] if we ask for [puppetmasterd]" do - Puppet.expects(:warning) - @settings.set_value(:myval, "foo", :puppetmasterd) - - @settings.stubs(:mode).returns(:master) - @settings.value(:myval).should == "foo" - end - - it "should warn and use [agent] if we ask for [puppetd]" do - Puppet.expects(:warning) - @settings.set_value(:myval, "foo", :puppetd) - - @settings.stubs(:mode).returns(:agent) - @settings.value(:myval).should == "foo" - end - end - - describe "when returning values" do - before do - @settings = Puppet::Util::Settings.new - @settings.setdefaults :section, :config => ["/my/file", "eh"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"], :four => ["$two $three FOUR", "d"] - FileTest.stubs(:exist?).returns true - end - - it "should provide a mechanism for returning set values" do - @settings[:one] = "other" - @settings[:one].should == "other" - end - - it "should interpolate default values for other parameters into returned parameter values" do - @settings[:one].should == "ONE" - @settings[:two].should == "ONE TWO" - @settings[:three].should == "ONE ONE TWO THREE" - end - - it "should interpolate default values that themselves need to be interpolated" do - @settings[:four].should == "ONE TWO ONE ONE TWO THREE FOUR" - end - - it "should provide a method for returning uninterpolated values" do - @settings[:two] = "$one tw0" - @settings.uninterpolated_value(:two).should == "$one tw0" - @settings.uninterpolated_value(:four).should == "$two $three FOUR" - end - - it "should interpolate set values for other parameters into returned parameter values" do - @settings[:one] = "on3" - @settings[:two] = "$one tw0" - @settings[:three] = "$one $two thr33" - @settings[:four] = "$one $two $three f0ur" - @settings[:one].should == "on3" - @settings[:two].should == "on3 tw0" - @settings[:three].should == "on3 on3 tw0 thr33" - @settings[:four].should == "on3 on3 tw0 on3 on3 tw0 thr33 f0ur" - end - - it "should not cache interpolated values such that stale information is returned" do - @settings[:two].should == "ONE TWO" - @settings[:one] = "one" - @settings[:two].should == "one TWO" - end - - it "should not cache values such that information from one environment is returned for another environment" do - text = "[env1]\none = oneval\n[env2]\none = twoval\n" - @settings.stubs(:read_file).returns(text) - @settings.parse - - @settings.value(:one, "env1").should == "oneval" - @settings.value(:one, "env2").should == "twoval" - end - - it "should have a mode determined by the 'mode' parameter that cannot be edited" do - @settings.setdefaults(:whatever, :mode => ["something", "yayness"]) - @settings.mode.should == :something - - lambda{ @settings[:mode] = :other }.should raise_error - end - end - - describe "when choosing which value to return" do - before do - @settings = Puppet::Util::Settings.new - @settings.setdefaults :section, - :config => ["/my/file", "a"], - :one => ["ONE", "a"], - :mode => ["mymode", "w"] - FileTest.stubs(:exist?).returns true - end - - it "should return default values if no values have been set" do - @settings[:one].should == "ONE" - end - - it "should return values set on the cli before values set in the configuration file" do - text = "[main]\none = fileval\n" - @settings.stubs(:read_file).returns(text) - @settings.handlearg("--one", "clival") - @settings.parse - - @settings[:one].should == "clival" - end - - it "should return values set on the cli before values set in Ruby" do - @settings[:one] = "rubyval" - @settings.handlearg("--one", "clival") - @settings[:one].should == "clival" - end - - it "should return values set in the mode-specific section before values set in the main section" do - text = "[main]\none = mainval\n[mymode]\none = modeval\n" - @settings.stubs(:read_file).returns(text) - @settings.parse - - @settings[:one].should == "modeval" - end - - it "should not return values outside of its search path" do - text = "[other]\none = oval\n" - file = "/some/file" - @settings.stubs(:read_file).returns(text) - @settings.parse - @settings[:one].should == "ONE" - end - - it "should return values in a specified environment" do - text = "[env]\none = envval\n" - @settings.stubs(:read_file).returns(text) - @settings.parse - @settings.value(:one, "env").should == "envval" - end - - it "should interpolate found values using the current environment" do - @settings.setdefaults :main, :myval => ["$environment/foo", "mydocs"] - - @settings.value(:myval, "myenv").should == "myenv/foo" - end - - it "should return values in a specified environment before values in the main or name sections" do - text = "[env]\none = envval\n[main]\none = mainval\n[myname]\none = nameval\n" - @settings.stubs(:read_file).returns(text) - @settings.parse - @settings.value(:one, "env").should == "envval" - end - end - - describe "when parsing its configuration" do - before do - @settings = Puppet::Util::Settings.new - @settings.stubs(:service_user_available?).returns true - @file = "/some/file" - @settings.setdefaults :section, :user => ["suser", "doc"], :group => ["sgroup", "doc"] - @settings.setdefaults :section, :config => ["/some/file", "eh"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"] - FileTest.stubs(:exist?).returns true - end - - it "should use its current ':config' value for the file to parse" do - myfile = Puppet.features.posix? ? "/my/file" : "C:/myfile" # do not stub expand_path here, as this leads to a stack overflow, when mocha tries to use it - @settings[:config] = myfile - - File.expects(:read).with(myfile).returns "[main]" - - @settings.parse - end - - it "should fail if no configuration setting is defined" do - @settings = Puppet::Util::Settings.new - lambda { @settings.parse }.should raise_error(RuntimeError) - end - - it "should not try to parse non-existent files" do - FileTest.expects(:exist?).with("/some/file").returns false - - File.expects(:read).with("/some/file").never - - @settings.parse - end - - it "should set a timer that triggers reparsing, even if the file does not exist" do - FileTest.expects(:exist?).returns false - @settings.expects(:set_filetimeout_timer) - - @settings.parse - end - - it "should return values set in the configuration file" do - text = "[main] - one = fileval - " - @settings.expects(:read_file).returns(text) - @settings.parse - @settings[:one].should == "fileval" - end - - #484 - this should probably be in the regression area - it "should not throw an exception on unknown parameters" do - text = "[main]\nnosuchparam = mval\n" - @settings.expects(:read_file).returns(text) - lambda { @settings.parse }.should_not raise_error - end - - it "should convert booleans in the configuration file into Ruby booleans" do - text = "[main] - one = true - two = false - " - @settings.expects(:read_file).returns(text) - @settings.parse - @settings[:one].should == true - @settings[:two].should == false - end - - it "should convert integers in the configuration file into Ruby Integers" do - text = "[main] - one = 65 - " - @settings.expects(:read_file).returns(text) - @settings.parse - @settings[:one].should == 65 - end - - it "should support specifying all metadata (owner, group, mode) in the configuration file" do - @settings.setdefaults :section, :myfile => ["/myfile", "a"] - - text = "[main] - myfile = /other/file {owner = service, group = service, mode = 644} - " - @settings.expects(:read_file).returns(text) - @settings.parse - @settings[:myfile].should == "/other/file" - @settings.metadata(:myfile).should == {:owner => "suser", :group => "sgroup", :mode => "644"} - end - - it "should support specifying a single piece of metadata (owner, group, or mode) in the configuration file" do - @settings.setdefaults :section, :myfile => ["/myfile", "a"] - - text = "[main] - myfile = /other/file {owner = service} - " - file = "/some/file" - @settings.expects(:read_file).returns(text) - @settings.parse - @settings[:myfile].should == "/other/file" - @settings.metadata(:myfile).should == {:owner => "suser"} - end - - it "should call hooks associated with values set in the configuration file" do - values = [] - @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} - - text = "[main] - mysetting = setval - " - @settings.expects(:read_file).returns(text) - @settings.parse - values.should == ["setval"] - end - - it "should not call the same hook for values set multiple times in the configuration file" do - values = [] - @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} - - text = "[main] - mysetting = setval - [puppet] - mysetting = other - " - @settings.expects(:read_file).returns(text) - @settings.parse - values.should == ["setval"] - end - - it "should pass the environment-specific value to the hook when one is available" do - values = [] - @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} - @settings.setdefaults :section, :environment => ["yay", "a"] - @settings.setdefaults :section, :environments => ["yay,foo", "a"] - - text = "[main] - mysetting = setval - [yay] - mysetting = other - " - @settings.expects(:read_file).returns(text) - @settings.parse - values.should == ["other"] - end - - it "should pass the interpolated value to the hook when one is available" do - values = [] - @settings.setdefaults :section, :base => {:default => "yay", :desc => "a", :hook => proc { |v| values << v }} - @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} - - text = "[main] - mysetting = $base/setval - " - @settings.expects(:read_file).returns(text) - @settings.parse - values.should == ["yay/setval"] - end - - it "should allow empty values" do - @settings.setdefaults :section, :myarg => ["myfile", "a"] - - text = "[main] - myarg = - " - @settings.stubs(:read_file).returns(text) - @settings.parse - @settings[:myarg].should == "" - end - - describe "and when reading a non-positive filetimeout value from the config file" do - before do - @settings.setdefaults :foo, :filetimeout => [5, "eh"] - - somefile = "/some/file" - text = "[main] - filetimeout = -1 - " - File.expects(:read).with(somefile).returns(text) - File.expects(:expand_path).with(somefile).returns somefile - @settings[:config] = somefile - end - - it "should not set a timer" do - EventLoop::Timer.expects(:new).never - - @settings.parse - end - end - end - - describe "when reparsing its configuration" do - before do - @settings = Puppet::Util::Settings.new - @settings.setdefaults :section, :config => ["/test/file", "a"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"] - FileTest.stubs(:exist?).returns true - end - - it "should use a LoadedFile instance to determine if the file has changed" do - file = mock 'file' - Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file - - file.expects(:changed?) - - @settings.stubs(:parse) - @settings.reparse - end - - it "should not create the LoadedFile instance and should not parse if the file does not exist" do - FileTest.expects(:exist?).with("/test/file").returns false - Puppet::Util::LoadedFile.expects(:new).never - - @settings.expects(:parse).never - - @settings.reparse - end - - it "should not reparse if the file has not changed" do - file = mock 'file' - Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file - - file.expects(:changed?).returns false - - @settings.expects(:parse).never - - @settings.reparse - end - - it "should reparse if the file has changed" do - file = stub 'file', :file => "/test/file" - Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file - - file.expects(:changed?).returns true - - @settings.expects(:parse) - - @settings.reparse - end - - it "should use a cached LoadedFile instance" do - first = mock 'first' - second = mock 'second' - Puppet::Util::LoadedFile.expects(:new).times(2).with("/test/file").returns(first).then.returns(second) - - @settings.file.should equal(first) - Puppet::Util::Cacher.expire - @settings.file.should equal(second) - end - - it "should replace in-memory values with on-file values" do - # Init the value - text = "[main]\none = disk-init\n" - file = mock 'file' - file.stubs(:changed?).returns(true) - file.stubs(:file).returns("/test/file") - @settings[:one] = "init" - @settings.file = file - - # Now replace the value - text = "[main]\none = disk-replace\n" - - # This is kinda ridiculous - the reason it parses twice is that - # it goes to parse again when we ask for the value, because the - # mock always says it should get reparsed. - @settings.stubs(:read_file).returns(text) - @settings.reparse - @settings[:one].should == "disk-replace" - end - - it "should retain parameters set by cli when configuration files are reparsed" do - @settings.handlearg("--one", "clival") - - text = "[main]\none = on-disk\n" - @settings.stubs(:read_file).returns(text) - @settings.parse - - @settings[:one].should == "clival" - end - - it "should remove in-memory values that are no longer set in the file" do - # Init the value - text = "[main]\none = disk-init\n" - @settings.expects(:read_file).returns(text) - @settings.parse - @settings[:one].should == "disk-init" - - # Now replace the value - text = "[main]\ntwo = disk-replace\n" - @settings.expects(:read_file).returns(text) - @settings.parse - #@settings.reparse - - # The originally-overridden value should be replaced with the default - @settings[:one].should == "ONE" - - # and we should now have the new value in memory - @settings[:two].should == "disk-replace" - end - - it "should retain in-memory values if the file has a syntax error" do - # Init the value - text = "[main]\none = initial-value\n" - @settings.expects(:read_file).returns(text) - @settings.parse - @settings[:one].should == "initial-value" - - # Now replace the value with something bogus - text = "[main]\nkenny = killed-by-what-follows\n1 is 2, blah blah florp\n" - @settings.expects(:read_file).returns(text) - @settings.parse - - # The originally-overridden value should not be replaced with the default - @settings[:one].should == "initial-value" - - # and we should not have the new value in memory - @settings[:kenny].should be_nil - end - end - - it "should provide a method for creating a catalog of resources from its configuration" do - Puppet::Util::Settings.new.should respond_to(:to_catalog) - end - - describe "when creating a catalog" do - before do - @settings = Puppet::Util::Settings.new - @settings.stubs(:service_user_available?).returns true - @prefix = Puppet.features.posix? ? "" : "C:" - end - - it "should add all file resources to the catalog if no sections have been specified" do - @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"], :seconddir => [@prefix+"/seconddir", "a"] - @settings.setdefaults :other, :otherdir => [@prefix+"/otherdir", "a"] - - catalog = @settings.to_catalog - - [@prefix+"/maindir", @prefix+"/seconddir", @prefix+"/otherdir"].each do |path| - catalog.resource(:file, path).should be_instance_of(Puppet::Resource) - end - end - - it "should add only files in the specified sections if section names are provided" do - @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] - @settings.setdefaults :other, :otherdir => [@prefix+"/otherdir", "a"] - catalog = @settings.to_catalog(:main) - catalog.resource(:file, @prefix+"/otherdir").should be_nil - catalog.resource(:file, @prefix+"/maindir").should be_instance_of(Puppet::Resource) - end - - it "should not try to add the same file twice" do - @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] - @settings.setdefaults :other, :otherdir => [@prefix+"/maindir", "a"] - lambda { @settings.to_catalog }.should_not raise_error - end - - it "should ignore files whose :to_resource method returns nil" do - @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] - @settings.setting(:maindir).expects(:to_resource).returns nil - - Puppet::Resource::Catalog.any_instance.expects(:add_resource).never - @settings.to_catalog - end - - describe "when adding users and groups to the catalog" do - before do - Puppet.features.stubs(:root?).returns true - @settings.setdefaults :foo, :mkusers => [true, "e"], :user => ["suser", "doc"], :group => ["sgroup", "doc"] - @settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} - - @catalog = @settings.to_catalog - end - - it "should add each specified user and group to the catalog if :mkusers is a valid setting, is enabled, and we're running as root" do - @catalog.resource(:user, "suser").should be_instance_of(Puppet::Resource) - @catalog.resource(:group, "sgroup").should be_instance_of(Puppet::Resource) - end - - it "should only add users and groups to the catalog from specified sections" do - @settings.setdefaults :yay, :yaydir => {:default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} - catalog = @settings.to_catalog(:other) - catalog.resource(:user, "jane").should be_nil - catalog.resource(:group, "billy").should be_nil - end - - it "should not add users or groups to the catalog if :mkusers not running as root" do - Puppet.features.stubs(:root?).returns false - - catalog = @settings.to_catalog - catalog.resource(:user, "suser").should be_nil - catalog.resource(:group, "sgroup").should be_nil - end - - it "should not add users or groups to the catalog if :mkusers is not a valid setting" do - Puppet.features.stubs(:root?).returns true - settings = Puppet::Util::Settings.new - settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} - - catalog = settings.to_catalog - catalog.resource(:user, "suser").should be_nil - catalog.resource(:group, "sgroup").should be_nil - end - - it "should not add users or groups to the catalog if :mkusers is a valid setting but is disabled" do - @settings[:mkusers] = false - - catalog = @settings.to_catalog - catalog.resource(:user, "suser").should be_nil - catalog.resource(:group, "sgroup").should be_nil - end - - it "should not try to add users or groups to the catalog twice" do - @settings.setdefaults :yay, :yaydir => {:default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} - - # This would fail if users/groups were added twice - lambda { @settings.to_catalog }.should_not raise_error - end - - it "should set :ensure to :present on each created user and group" do - @catalog.resource(:user, "suser")[:ensure].should == :present - @catalog.resource(:group, "sgroup")[:ensure].should == :present - end - - it "should set each created user's :gid to the service group" do - @settings.to_catalog.resource(:user, "suser")[:gid].should == "sgroup" - end - - it "should not attempt to manage the root user" do - Puppet.features.stubs(:root?).returns true - @settings.setdefaults :foo, :foodir => {:default => "/foodir", :desc => "a", :owner => "root", :group => "service"} - - @settings.to_catalog.resource(:user, "root").should be_nil - end - end - end - - it "should be able to be converted to a manifest" do - Puppet::Util::Settings.new.should respond_to(:to_manifest) - end - - describe "when being converted to a manifest" do - it "should produce a string with the code for each resource joined by two carriage returns" do - @settings = Puppet::Util::Settings.new - @settings.setdefaults :main, :maindir => ["/maindir", "a"], :seconddir => ["/seconddir", "a"] - - main = stub 'main_resource', :ref => "File[/maindir]" - main.expects(:to_manifest).returns "maindir" - second = stub 'second_resource', :ref => "File[/seconddir]" - second.expects(:to_manifest).returns "seconddir" - @settings.setting(:maindir).expects(:to_resource).returns main - @settings.setting(:seconddir).expects(:to_resource).returns second - - @settings.to_manifest.split("\n\n").sort.should == %w{maindir seconddir} - end - end - - describe "when using sections of the configuration to manage the local host" do - before do - @settings = Puppet::Util::Settings.new - @settings.stubs(:service_user_available?).returns true - @settings.setdefaults :main, :noop => [false, ""] - @settings.setdefaults :main, :maindir => ["/maindir", "a"], :seconddir => ["/seconddir", "a"] - @settings.setdefaults :main, :user => ["suser", "doc"], :group => ["sgroup", "doc"] - @settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service", :mode => 0755} - @settings.setdefaults :third, :thirddir => ["/thirddir", "b"] - @settings.setdefaults :files, :myfile => {:default => "/myfile", :desc => "a", :mode => 0755} - end - - it "should provide a method that writes files with the correct modes" do - @settings.should respond_to(:write) - end - - it "should provide a method that creates directories with the correct modes" do - Puppet::Util::SUIDManager.expects(:asuser).with("suser", "sgroup").yields - Dir.expects(:mkdir).with("/otherdir", 0755) - @settings.mkdir(:otherdir) - end - - it "should create a catalog with the specified sections" do - @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") - @settings.use(:main, :other) - end - - it "should canonicalize the sections" do - @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") - @settings.use("main", "other") - end - - it "should ignore sections that have already been used" do - @settings.expects(:to_catalog).with(:main).returns Puppet::Resource::Catalog.new("foo") - @settings.use(:main) - @settings.expects(:to_catalog).with(:other).returns Puppet::Resource::Catalog.new("foo") - @settings.use(:main, :other) - end - - it "should ignore tags and schedules when creating files and directories" - - it "should be able to provide all of its parameters in a format compatible with GetOpt::Long" do - pending "Not converted from test/unit yet" - end - - it "should convert the created catalog to a RAL catalog" do - @catalog = Puppet::Resource::Catalog.new("foo") - @settings.expects(:to_catalog).with(:main).returns @catalog - - @catalog.expects(:to_ral).returns @catalog - @settings.use(:main) - end - - it "should specify that it is not managing a host catalog" do - catalog = Puppet::Resource::Catalog.new("foo") - @settings.expects(:to_catalog).returns catalog - - catalog.stubs(:to_ral).returns catalog - - catalog.expects(:host_config=).with false - - @settings.use(:main) - end - - it "should support a method for re-using all currently used sections" do - @settings.expects(:to_catalog).with(:main, :third).times(2).returns Puppet::Resource::Catalog.new("foo") - - @settings.use(:main, :third) - @settings.reuse - end - - it "should fail with an appropriate message if any resources fail" do - @catalog = Puppet::Resource::Catalog.new("foo") - @catalog.stubs(:to_ral).returns @catalog - @settings.expects(:to_catalog).returns @catalog - - @trans = mock("transaction") - @catalog.expects(:apply).yields(@trans) - - @trans.expects(:any_failed?).returns(true) - - report = mock 'report' - @trans.expects(:report).returns report - - log = mock 'log', :to_s => "My failure", :level => :err - report.expects(:logs).returns [log] - - @settings.expects(:raise).with { |msg| msg.include?("My failure") } - @settings.use(:whatever) - end - end - - describe "when dealing with printing configs" do - before do - @settings = Puppet::Util::Settings.new - #these are the magic default values - @settings.stubs(:value).with(:configprint).returns("") - @settings.stubs(:value).with(:genconfig).returns(false) - @settings.stubs(:value).with(:genmanifest).returns(false) - @settings.stubs(:value).with(:environment).returns(nil) - end - - describe "when checking print_config?" do - it "should return false when the :configprint, :genconfig and :genmanifest are not set" do - @settings.print_configs?.should be_false - end - - it "should return true when :configprint has a value" do - @settings.stubs(:value).with(:configprint).returns("something") - @settings.print_configs?.should be_true - end - - it "should return true when :genconfig has a value" do - @settings.stubs(:value).with(:genconfig).returns(true) - @settings.print_configs?.should be_true - end - - it "should return true when :genmanifest has a value" do - @settings.stubs(:value).with(:genmanifest).returns(true) - @settings.print_configs?.should be_true - end - end - - describe "when printing configs" do - describe "when :configprint has a value" do - it "should call print_config_options" do - @settings.stubs(:value).with(:configprint).returns("something") - @settings.expects(:print_config_options) - @settings.print_configs - end - - it "should get the value of the option using the environment" do - @settings.stubs(:value).with(:configprint).returns("something") - @settings.stubs(:include?).with("something").returns(true) - @settings.expects(:value).with(:environment).returns("env") - @settings.expects(:value).with("something", "env").returns("foo") - @settings.stubs(:puts).with("foo") - @settings.print_configs - end - - it "should print the value of the option" do - @settings.stubs(:value).with(:configprint).returns("something") - @settings.stubs(:include?).with("something").returns(true) - @settings.stubs(:value).with("something", nil).returns("foo") - @settings.expects(:puts).with("foo") - @settings.print_configs - end - - it "should print the value pairs if there are multiple options" do - @settings.stubs(:value).with(:configprint).returns("bar,baz") - @settings.stubs(:include?).with("bar").returns(true) - @settings.stubs(:include?).with("baz").returns(true) - @settings.stubs(:value).with("bar", nil).returns("foo") - @settings.stubs(:value).with("baz", nil).returns("fud") - @settings.expects(:puts).with("bar = foo") - @settings.expects(:puts).with("baz = fud") - @settings.print_configs - end - - it "should print a whole bunch of stuff if :configprint = all" - - it "should return true after printing" do - @settings.stubs(:value).with(:configprint).returns("something") - @settings.stubs(:include?).with("something").returns(true) - @settings.stubs(:value).with("something", nil).returns("foo") - @settings.stubs(:puts).with("foo") - @settings.print_configs.should be_true - end - - it "should return false if a config param is not found" do - @settings.stubs :puts - @settings.stubs(:value).with(:configprint).returns("something") - @settings.stubs(:include?).with("something").returns(false) - @settings.print_configs.should be_false - end - end - - describe "when genconfig is true" do - before do - @settings.stubs :puts - end - - it "should call to_config" do - @settings.stubs(:value).with(:genconfig).returns(true) - @settings.expects(:to_config) - @settings.print_configs - end - - it "should return true from print_configs" do - @settings.stubs(:value).with(:genconfig).returns(true) - @settings.stubs(:to_config) - @settings.print_configs.should be_true - end - end - - describe "when genmanifest is true" do - before do - @settings.stubs :puts - end - - it "should call to_config" do - @settings.stubs(:value).with(:genmanifest).returns(true) - @settings.expects(:to_manifest) - @settings.print_configs - end - - it "should return true from print_configs" do - @settings.stubs(:value).with(:genmanifest).returns(true) - @settings.stubs(:to_manifest) - @settings.print_configs.should be_true - end - end - end - end - - describe "when setting a timer to trigger configuration file reparsing" do - before do - @settings = Puppet::Util::Settings.new - @settings.setdefaults :foo, :filetimeout => [5, "eh"] - end - - it "should do nothing if no filetimeout setting is available" do - @settings.expects(:value).with(:filetimeout).returns nil - EventLoop::Timer.expects(:new).never - @settings.set_filetimeout_timer - end - - it "should always convert the timer interval to an integer" do - @settings.expects(:value).with(:filetimeout).returns "10" - EventLoop::Timer.expects(:new).with(:interval => 10, :start? => true, :tolerance => 1) - @settings.set_filetimeout_timer - end - - it "should do nothing if the filetimeout setting is not greater than 0" do - @settings.expects(:value).with(:filetimeout).returns -2 - EventLoop::Timer.expects(:new).never - @settings.set_filetimeout_timer - end - - it "should create a timer with its interval set to the filetimeout, start? set to true, and a tolerance of 1" do - @settings.expects(:value).with(:filetimeout).returns 5 - EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1) - - @settings.set_filetimeout_timer - end - - it "should reparse when the timer goes off" do - EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1).yields - - @settings.expects(:reparse) - - @settings.set_filetimeout_timer - end - end - - describe "when determining if the service user is available" do - it "should return false if there is no user setting" do - Puppet::Util::Settings.new.should_not be_service_user_available - end - - it "should return false if the user provider says the user is missing" do - settings = Puppet::Util::Settings.new - settings.setdefaults :main, :user => ["foo", "doc"] - - user = mock 'user' - user.expects(:exists?).returns false - - Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == "foo" }.returns user - - settings.should_not be_service_user_available - end - - it "should return true if the user provider says the user is present" do - settings = Puppet::Util::Settings.new - settings.setdefaults :main, :user => ["foo", "doc"] - - user = mock 'user' - user.expects(:exists?).returns true - - Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == "foo" }.returns user - - settings.should be_service_user_available - end - - it "should cache the result" - end - - describe "#without_noop" do - before do - @settings = Puppet::Util::Settings.new - @settings.setdefaults :main, :noop => [true, ""] - end - - it "should set noop to false for the duration of the block" do - @settings.without_noop { @settings.value(:noop, :cli).should be_false } - end - - it "should ensure that noop is returned to its previous value" do - @settings.without_noop { raise } rescue nil - @settings.value(:noop, :cli).should be_true - end - - it "should work even if no 'noop' setting is available" do - settings = Puppet::Util::Settings.new - stuff = nil - settings.without_noop { stuff = "yay" } - stuff.should == "yay" - end - end -end diff --git a/spec/unit/util/storage_spec.rb b/spec/unit/util/storage_spec.rb new file mode 100755 index 000000000..ce5bd202a --- /dev/null +++ b/spec/unit/util/storage_spec.rb @@ -0,0 +1,235 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'yaml' +require 'tempfile' + +require 'puppet/util/storage' + +describe Puppet::Util::Storage do + + before(:all) do + @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" + Puppet[:statedir] = Dir.tmpdir() + end + + after(:all) do + Puppet.settings.clear + end + + before(:each) do + Puppet::Util::Storage.clear() + end + + describe "when caching a symbol" do + it "should return an empty hash" do + Puppet::Util::Storage.cache(:yayness).should == {} + Puppet::Util::Storage.cache(:more_yayness).should == {} + end + + it "should add the symbol to its internal state" do + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.state().should == {:yayness=>{}} + end + + it "should not clobber existing state when caching additional objects" do + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.state().should == {:yayness=>{}} + Puppet::Util::Storage.cache(:bubblyness) + Puppet::Util::Storage.state().should == {:yayness=>{},:bubblyness=>{}} + end + end + + describe "when caching a Puppet::Type" do + before(:all) do + @file_test = Puppet::Type.type(:file).new(:name => @basepath+"/yayness", :check => %w{checksum type}) + @exec_test = Puppet::Type.type(:exec).new(:name => @basepath+"/bin/ls /yayness") + end + + it "should return an empty hash" do + Puppet::Util::Storage.cache(@file_test).should == {} + Puppet::Util::Storage.cache(@exec_test).should == {} + end + + it "should add the resource ref to its internal state" do + Puppet::Util::Storage.state().should == {} + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.state().should == {"File[#{@basepath}/yayness]"=>{}} + Puppet::Util::Storage.cache(@exec_test) + Puppet::Util::Storage.state().should == {"File[#{@basepath}/yayness]"=>{}, "Exec[#{@basepath}/bin/ls /yayness]"=>{}} + end + end + + describe "when caching something other than a resource or symbol" do + it "should cache by converting to a string" do + data = Puppet::Util::Storage.cache(42) + data[:yay] = true + Puppet::Util::Storage.cache("42")[:yay].should be_true + end + end + + it "should clear its internal state when clear() is called" do + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.state().should == {:yayness=>{}} + Puppet::Util::Storage.clear() + Puppet::Util::Storage.state().should == {} + end + + describe "when loading from the state file" do + before do + Puppet.settings.stubs(:use).returns(true) + end + + describe "when the state file/directory does not exist" do + before(:each) do + transient = Tempfile.new('storage_test') + @path = transient.path() + transient.close!() + end + + it "should not fail to load()" do + FileTest.exists?(@path).should be_false() + Puppet[:statedir] = @path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet[:statefile] = @path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + end + + it "should not lose its internal state when load() is called" do + FileTest.exists?(@path).should be_false() + + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.state().should == {:yayness=>{}} + + Puppet[:statefile] = @path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + Puppet::Util::Storage.state().should == {:yayness=>{}} + end + end + + describe "when the state file/directory exists" do + before(:each) do + @state_file = Tempfile.new('storage_test') + @saved_statefile = Puppet[:statefile] + Puppet[:statefile] = @state_file.path() + end + + it "should overwrite its internal state if load() is called" do + # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.state().should == {:yayness=>{}} + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.state().should == {} + end + + it "should restore its internal state if the state file contains valid YAML" do + test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} + YAML.expects(:load).returns(test_yaml) + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.state().should == test_yaml + end + + it "should initialize with a clear internal state if the state file does not contain valid YAML" do + @state_file.write(:booness) + @state_file.flush() + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.state().should == {} + end + + it "should raise an error if the state file does not contain valid YAML and cannot be renamed" do + @state_file.write(:booness) + @state_file.flush() + YAML.expects(:load).raises(Puppet::Error) + File.expects(:rename).raises(SystemCallError) + + proc { Puppet::Util::Storage.load() }.should raise_error() + end + + it "should attempt to rename the state file if the file is corrupted" do + # We fake corruption by causing YAML.load to raise an exception + YAML.expects(:load).raises(Puppet::Error) + File.expects(:rename).at_least_once + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + end + + it "should fail gracefully on load() if the state file is not a regular file" do + @state_file.close!() + Dir.mkdir(Puppet[:statefile]) + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + Dir.rmdir(Puppet[:statefile]) + end + + it "should fail gracefully on load() if it cannot get a read lock on the state file" do + Puppet::Util::FileLocking.expects(:readlock).yields(false) + test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} + YAML.expects(:load).returns(test_yaml) + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.state().should == test_yaml + end + + after(:each) do + @state_file.close!() + Puppet[:statefile] = @saved_statefile + end + end + end + + describe "when storing to the state file" do + before(:each) do + @state_file = Tempfile.new('storage_test') + @saved_statefile = Puppet[:statefile] + Puppet[:statefile] = @state_file.path() + end + + it "should create the state file if it does not exist" do + @state_file.close!() + FileTest.exists?(Puppet[:statefile]).should be_false() + Puppet::Util::Storage.cache(:yayness) + + proc { Puppet::Util::Storage.store() }.should_not raise_error() + FileTest.exists?(Puppet[:statefile]).should be_true() + end + + it "should raise an exception if the state file is not a regular file" do + @state_file.close!() + Dir.mkdir(Puppet[:statefile]) + Puppet::Util::Storage.cache(:yayness) + + proc { Puppet::Util::Storage.store() }.should raise_error() + + Dir.rmdir(Puppet[:statefile]) + end + + it "should raise an exception if it cannot get a write lock on the state file" do + Puppet::Util::FileLocking.expects(:writelock).yields(false) + Puppet::Util::Storage.cache(:yayness) + + proc { Puppet::Util::Storage.store() }.should raise_error() + end + + it "should load() the same information that it store()s" do + Puppet::Util::Storage.cache(:yayness) + + Puppet::Util::Storage.state().should == {:yayness=>{}} + proc { Puppet::Util::Storage.store() }.should_not raise_error() + Puppet::Util::Storage.clear() + Puppet::Util::Storage.state().should == {} + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.state().should == {:yayness=>{}} + end + + after(:each) do + @state_file.close!() + Puppet[:statefile] = @saved_statefile + end + end +end diff --git a/spec/unit/util/storage_spec_spec.rb b/spec/unit/util/storage_spec_spec.rb deleted file mode 100755 index ce5bd202a..000000000 --- a/spec/unit/util/storage_spec_spec.rb +++ /dev/null @@ -1,235 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'yaml' -require 'tempfile' - -require 'puppet/util/storage' - -describe Puppet::Util::Storage do - - before(:all) do - @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" - Puppet[:statedir] = Dir.tmpdir() - end - - after(:all) do - Puppet.settings.clear - end - - before(:each) do - Puppet::Util::Storage.clear() - end - - describe "when caching a symbol" do - it "should return an empty hash" do - Puppet::Util::Storage.cache(:yayness).should == {} - Puppet::Util::Storage.cache(:more_yayness).should == {} - end - - it "should add the symbol to its internal state" do - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.state().should == {:yayness=>{}} - end - - it "should not clobber existing state when caching additional objects" do - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.state().should == {:yayness=>{}} - Puppet::Util::Storage.cache(:bubblyness) - Puppet::Util::Storage.state().should == {:yayness=>{},:bubblyness=>{}} - end - end - - describe "when caching a Puppet::Type" do - before(:all) do - @file_test = Puppet::Type.type(:file).new(:name => @basepath+"/yayness", :check => %w{checksum type}) - @exec_test = Puppet::Type.type(:exec).new(:name => @basepath+"/bin/ls /yayness") - end - - it "should return an empty hash" do - Puppet::Util::Storage.cache(@file_test).should == {} - Puppet::Util::Storage.cache(@exec_test).should == {} - end - - it "should add the resource ref to its internal state" do - Puppet::Util::Storage.state().should == {} - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.state().should == {"File[#{@basepath}/yayness]"=>{}} - Puppet::Util::Storage.cache(@exec_test) - Puppet::Util::Storage.state().should == {"File[#{@basepath}/yayness]"=>{}, "Exec[#{@basepath}/bin/ls /yayness]"=>{}} - end - end - - describe "when caching something other than a resource or symbol" do - it "should cache by converting to a string" do - data = Puppet::Util::Storage.cache(42) - data[:yay] = true - Puppet::Util::Storage.cache("42")[:yay].should be_true - end - end - - it "should clear its internal state when clear() is called" do - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.state().should == {:yayness=>{}} - Puppet::Util::Storage.clear() - Puppet::Util::Storage.state().should == {} - end - - describe "when loading from the state file" do - before do - Puppet.settings.stubs(:use).returns(true) - end - - describe "when the state file/directory does not exist" do - before(:each) do - transient = Tempfile.new('storage_test') - @path = transient.path() - transient.close!() - end - - it "should not fail to load()" do - FileTest.exists?(@path).should be_false() - Puppet[:statedir] = @path - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet[:statefile] = @path - proc { Puppet::Util::Storage.load() }.should_not raise_error() - end - - it "should not lose its internal state when load() is called" do - FileTest.exists?(@path).should be_false() - - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.state().should == {:yayness=>{}} - - Puppet[:statefile] = @path - proc { Puppet::Util::Storage.load() }.should_not raise_error() - - Puppet::Util::Storage.state().should == {:yayness=>{}} - end - end - - describe "when the state file/directory exists" do - before(:each) do - @state_file = Tempfile.new('storage_test') - @saved_statefile = Puppet[:statefile] - Puppet[:statefile] = @state_file.path() - end - - it "should overwrite its internal state if load() is called" do - # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.state().should == {:yayness=>{}} - - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.state().should == {} - end - - it "should restore its internal state if the state file contains valid YAML" do - test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} - YAML.expects(:load).returns(test_yaml) - - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.state().should == test_yaml - end - - it "should initialize with a clear internal state if the state file does not contain valid YAML" do - @state_file.write(:booness) - @state_file.flush() - - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.state().should == {} - end - - it "should raise an error if the state file does not contain valid YAML and cannot be renamed" do - @state_file.write(:booness) - @state_file.flush() - YAML.expects(:load).raises(Puppet::Error) - File.expects(:rename).raises(SystemCallError) - - proc { Puppet::Util::Storage.load() }.should raise_error() - end - - it "should attempt to rename the state file if the file is corrupted" do - # We fake corruption by causing YAML.load to raise an exception - YAML.expects(:load).raises(Puppet::Error) - File.expects(:rename).at_least_once - - proc { Puppet::Util::Storage.load() }.should_not raise_error() - end - - it "should fail gracefully on load() if the state file is not a regular file" do - @state_file.close!() - Dir.mkdir(Puppet[:statefile]) - - proc { Puppet::Util::Storage.load() }.should_not raise_error() - - Dir.rmdir(Puppet[:statefile]) - end - - it "should fail gracefully on load() if it cannot get a read lock on the state file" do - Puppet::Util::FileLocking.expects(:readlock).yields(false) - test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} - YAML.expects(:load).returns(test_yaml) - - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.state().should == test_yaml - end - - after(:each) do - @state_file.close!() - Puppet[:statefile] = @saved_statefile - end - end - end - - describe "when storing to the state file" do - before(:each) do - @state_file = Tempfile.new('storage_test') - @saved_statefile = Puppet[:statefile] - Puppet[:statefile] = @state_file.path() - end - - it "should create the state file if it does not exist" do - @state_file.close!() - FileTest.exists?(Puppet[:statefile]).should be_false() - Puppet::Util::Storage.cache(:yayness) - - proc { Puppet::Util::Storage.store() }.should_not raise_error() - FileTest.exists?(Puppet[:statefile]).should be_true() - end - - it "should raise an exception if the state file is not a regular file" do - @state_file.close!() - Dir.mkdir(Puppet[:statefile]) - Puppet::Util::Storage.cache(:yayness) - - proc { Puppet::Util::Storage.store() }.should raise_error() - - Dir.rmdir(Puppet[:statefile]) - end - - it "should raise an exception if it cannot get a write lock on the state file" do - Puppet::Util::FileLocking.expects(:writelock).yields(false) - Puppet::Util::Storage.cache(:yayness) - - proc { Puppet::Util::Storage.store() }.should raise_error() - end - - it "should load() the same information that it store()s" do - Puppet::Util::Storage.cache(:yayness) - - Puppet::Util::Storage.state().should == {:yayness=>{}} - proc { Puppet::Util::Storage.store() }.should_not raise_error() - Puppet::Util::Storage.clear() - Puppet::Util::Storage.state().should == {} - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.state().should == {:yayness=>{}} - end - - after(:each) do - @state_file.close!() - Puppet[:statefile] = @saved_statefile - end - end -end diff --git a/spec/unit/util/tagging_spec.rb b/spec/unit/util/tagging_spec.rb new file mode 100755 index 000000000..04800b378 --- /dev/null +++ b/spec/unit/util/tagging_spec.rb @@ -0,0 +1,102 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-01-19. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/tagging' + +describe Puppet::Util::Tagging, "when adding tags" do + before do + @tagger = Object.new + @tagger.extend(Puppet::Util::Tagging) + end + + it "should have a method for adding tags" do + @tagger.should be_respond_to(:tag) + end + + it "should have a method for returning all tags" do + @tagger.should be_respond_to(:tags) + end + + it "should add tags to the returned tag list" do + @tagger.tag("one") + @tagger.tags.should be_include("one") + end + + it "should not add duplicate tags to the returned tag list" do + @tagger.tag("one") + @tagger.tag("one") + @tagger.tags.should == ["one"] + end + + it "should return a duplicate of the tag list, rather than the original" do + @tagger.tag("one") + tags = @tagger.tags + tags << "two" + @tagger.tags.should_not be_include("two") + end + + it "should add all provided tags to the tag list" do + @tagger.tag("one", "two") + @tagger.tags.should be_include("one") + @tagger.tags.should be_include("two") + end + + it "should fail on tags containing '*' characters" do + lambda { @tagger.tag("bad*tag") }.should raise_error(Puppet::ParseError) + end + + it "should fail on tags starting with '-' characters" do + lambda { @tagger.tag("-badtag") }.should raise_error(Puppet::ParseError) + end + + it "should fail on tags containing ' ' characters" do + lambda { @tagger.tag("bad tag") }.should raise_error(Puppet::ParseError) + end + + it "should allow alpha tags" do + lambda { @tagger.tag("good_tag") }.should_not raise_error(Puppet::ParseError) + end + + it "should allow tags containing '.' characters" do + lambda { @tagger.tag("good.tag") }.should_not raise_error(Puppet::ParseError) + end + + it "should provide a method for testing tag validity" do + @tagger.singleton_class.publicize_methods(:valid_tag?) { @tagger.should be_respond_to(:valid_tag?) } + end + + it "should add qualified classes as tags" do + @tagger.tag("one::two") + @tagger.tags.should be_include("one::two") + end + + it "should add each part of qualified classes as tags" do + @tagger.tag("one::two::three") + @tagger.tags.should be_include("one") + @tagger.tags.should be_include("two") + @tagger.tags.should be_include("three") + end + + it "should indicate when the object is tagged with a provided tag" do + @tagger.tag("one") + @tagger.should be_tagged("one") + end + + it "should indicate when the object is not tagged with a provided tag" do + @tagger.should_not be_tagged("one") + end + + it "should indicate when the object is tagged with any tag in an array" do + @tagger.tag("one") + @tagger.should be_tagged("one","two","three") + end + + it "should indicate when the object is not tagged with any tag in an array" do + @tagger.tag("one") + @tagger.should_not be_tagged("two","three") + end +end diff --git a/spec/unit/util/tagging_spec_spec.rb b/spec/unit/util/tagging_spec_spec.rb deleted file mode 100755 index 04800b378..000000000 --- a/spec/unit/util/tagging_spec_spec.rb +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2008-01-19. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/util/tagging' - -describe Puppet::Util::Tagging, "when adding tags" do - before do - @tagger = Object.new - @tagger.extend(Puppet::Util::Tagging) - end - - it "should have a method for adding tags" do - @tagger.should be_respond_to(:tag) - end - - it "should have a method for returning all tags" do - @tagger.should be_respond_to(:tags) - end - - it "should add tags to the returned tag list" do - @tagger.tag("one") - @tagger.tags.should be_include("one") - end - - it "should not add duplicate tags to the returned tag list" do - @tagger.tag("one") - @tagger.tag("one") - @tagger.tags.should == ["one"] - end - - it "should return a duplicate of the tag list, rather than the original" do - @tagger.tag("one") - tags = @tagger.tags - tags << "two" - @tagger.tags.should_not be_include("two") - end - - it "should add all provided tags to the tag list" do - @tagger.tag("one", "two") - @tagger.tags.should be_include("one") - @tagger.tags.should be_include("two") - end - - it "should fail on tags containing '*' characters" do - lambda { @tagger.tag("bad*tag") }.should raise_error(Puppet::ParseError) - end - - it "should fail on tags starting with '-' characters" do - lambda { @tagger.tag("-badtag") }.should raise_error(Puppet::ParseError) - end - - it "should fail on tags containing ' ' characters" do - lambda { @tagger.tag("bad tag") }.should raise_error(Puppet::ParseError) - end - - it "should allow alpha tags" do - lambda { @tagger.tag("good_tag") }.should_not raise_error(Puppet::ParseError) - end - - it "should allow tags containing '.' characters" do - lambda { @tagger.tag("good.tag") }.should_not raise_error(Puppet::ParseError) - end - - it "should provide a method for testing tag validity" do - @tagger.singleton_class.publicize_methods(:valid_tag?) { @tagger.should be_respond_to(:valid_tag?) } - end - - it "should add qualified classes as tags" do - @tagger.tag("one::two") - @tagger.tags.should be_include("one::two") - end - - it "should add each part of qualified classes as tags" do - @tagger.tag("one::two::three") - @tagger.tags.should be_include("one") - @tagger.tags.should be_include("two") - @tagger.tags.should be_include("three") - end - - it "should indicate when the object is tagged with a provided tag" do - @tagger.tag("one") - @tagger.should be_tagged("one") - end - - it "should indicate when the object is not tagged with a provided tag" do - @tagger.should_not be_tagged("one") - end - - it "should indicate when the object is tagged with any tag in an array" do - @tagger.tag("one") - @tagger.should be_tagged("one","two","three") - end - - it "should indicate when the object is not tagged with any tag in an array" do - @tagger.tag("one") - @tagger.should_not be_tagged("two","three") - end -end diff --git a/spec/unit/util/user_attr_spec.rb b/spec/unit/util/user_attr_spec.rb new file mode 100644 index 000000000..57787b1a6 --- /dev/null +++ b/spec/unit/util/user_attr_spec.rb @@ -0,0 +1,47 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/user_attr' + +describe UserAttr do + before do + user_attr = ["foo::::type=role", "bar::::type=normal;profile=foobar"] + File.stubs(:readlines).returns(user_attr) + end + + describe "when getting attributes by name" do + it "should return nil if there is no entry for that name" do + UserAttr.get_attributes_by_name('baz').should == nil + end + + it "should return a hash if there is an entry in /etc/user_attr" do + UserAttr.get_attributes_by_name('foo').class.should == Hash + end + + it "should return a hash with the name value from /etc/user_attr" do + UserAttr.get_attributes_by_name('foo')[:name].should == 'foo' + end + + #this test is contrived + #there are a bunch of possible parameters that could be in the hash + #the role/normal is just a the convention of the file + describe "when the name is a role" do + it "should contain :type = role" do + UserAttr.get_attributes_by_name('foo')[:type].should == 'role' + end + end + + describe "when the name is not a role" do + it "should contain :type = normal" do + UserAttr.get_attributes_by_name('bar')[:type].should == 'normal' + end + end + + describe "when the name has more attributes" do + it "should contain all the attributes" do + UserAttr.get_attributes_by_name('bar')[:profile].should == 'foobar' + end + end + end +end diff --git a/spec/unit/util/user_attr_spec_spec.rb b/spec/unit/util/user_attr_spec_spec.rb deleted file mode 100644 index 57787b1a6..000000000 --- a/spec/unit/util/user_attr_spec_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/user_attr' - -describe UserAttr do - before do - user_attr = ["foo::::type=role", "bar::::type=normal;profile=foobar"] - File.stubs(:readlines).returns(user_attr) - end - - describe "when getting attributes by name" do - it "should return nil if there is no entry for that name" do - UserAttr.get_attributes_by_name('baz').should == nil - end - - it "should return a hash if there is an entry in /etc/user_attr" do - UserAttr.get_attributes_by_name('foo').class.should == Hash - end - - it "should return a hash with the name value from /etc/user_attr" do - UserAttr.get_attributes_by_name('foo')[:name].should == 'foo' - end - - #this test is contrived - #there are a bunch of possible parameters that could be in the hash - #the role/normal is just a the convention of the file - describe "when the name is a role" do - it "should contain :type = role" do - UserAttr.get_attributes_by_name('foo')[:type].should == 'role' - end - end - - describe "when the name is not a role" do - it "should contain :type = normal" do - UserAttr.get_attributes_by_name('bar')[:type].should == 'normal' - end - end - - describe "when the name has more attributes" do - it "should contain all the attributes" do - UserAttr.get_attributes_by_name('bar')[:profile].should == 'foobar' - end - end - end -end diff --git a/spec/unit/util/warnings_spec.rb b/spec/unit/util/warnings_spec.rb new file mode 100755 index 000000000..15785cf58 --- /dev/null +++ b/spec/unit/util/warnings_spec.rb @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Util::Warnings do + before(:all) do + @msg1 = "booness" + @msg2 = "more booness" + end + + {:notice => "notice_once", :warning => "warnonce"}.each do |log, method| + describe "when registring '%s' messages" % log do + it "should always return nil" do + Puppet::Util::Warnings.send(method, @msg1).should be(nil) + end + + it "should issue a warning" do + Puppet.expects(log).with(@msg1) + Puppet::Util::Warnings.send(method, @msg1) + end + + it "should issue a warning exactly once per unique message" do + Puppet.expects(log).with(@msg1).once + Puppet::Util::Warnings.send(method, @msg1) + Puppet::Util::Warnings.send(method, @msg1) + end + + it "should issue multiple warnings for multiple unique messages" do + Puppet.expects(log).times(2) + Puppet::Util::Warnings.send(method, @msg1) + Puppet::Util::Warnings.send(method, @msg2) + end + end + end + + after(:each) do + Puppet::Util::Warnings.clear_warnings() + end +end diff --git a/spec/unit/util/warnings_spec_spec.rb b/spec/unit/util/warnings_spec_spec.rb deleted file mode 100755 index 15785cf58..000000000 --- a/spec/unit/util/warnings_spec_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Util::Warnings do - before(:all) do - @msg1 = "booness" - @msg2 = "more booness" - end - - {:notice => "notice_once", :warning => "warnonce"}.each do |log, method| - describe "when registring '%s' messages" % log do - it "should always return nil" do - Puppet::Util::Warnings.send(method, @msg1).should be(nil) - end - - it "should issue a warning" do - Puppet.expects(log).with(@msg1) - Puppet::Util::Warnings.send(method, @msg1) - end - - it "should issue a warning exactly once per unique message" do - Puppet.expects(log).with(@msg1).once - Puppet::Util::Warnings.send(method, @msg1) - Puppet::Util::Warnings.send(method, @msg1) - end - - it "should issue multiple warnings for multiple unique messages" do - Puppet.expects(log).times(2) - Puppet::Util::Warnings.send(method, @msg1) - Puppet::Util::Warnings.send(method, @msg2) - end - end - end - - after(:each) do - Puppet::Util::Warnings.clear_warnings() - end -end diff --git a/spec/unit/util/zaml_spec.rb b/spec/unit/util/zaml_spec.rb new file mode 100644 index 000000000..1f21c4e29 --- /dev/null +++ b/spec/unit/util/zaml_spec.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/util/monkey_patches' + +describe "Pure ruby yaml implementation" do + { + 7 => "--- 7", + 3.14159 => "--- 3.14159", + 'test' => "--- test", + [] => "--- []", + :symbol => "--- !ruby/sym symbol", + {:a => "A"} => "--- \n !ruby/sym a: A" + }.each { |o,y| + it "should convert the #{o.class} #{o.inspect} to yaml" do + o.to_yaml.should == y + end + it "should produce yaml for the #{o.class} #{o.inspect} that can be reconstituted" do + YAML.load(o.to_yaml).should == o + end + } + # + # Can't test for equality on raw objects + { + Object.new => "--- !ruby/object {}", + [Object.new] => "--- \n - !ruby/object {}", + {Object.new => Object.new} => "--- \n ? !ruby/object {}\n : !ruby/object {}" + }.each { |o,y| + it "should convert the #{o.class} #{o.inspect} to yaml" do + o.to_yaml.should == y + end + it "should produce yaml for the #{o.class} #{o.inspect} that can be reconstituted" do + lambda { YAML.load(o.to_yaml) }.should_not raise_error + end + } +end + diff --git a/spec/unit/util/zaml_spec_spec.rb b/spec/unit/util/zaml_spec_spec.rb deleted file mode 100644 index 1f21c4e29..000000000 --- a/spec/unit/util/zaml_spec_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/util/monkey_patches' - -describe "Pure ruby yaml implementation" do - { - 7 => "--- 7", - 3.14159 => "--- 3.14159", - 'test' => "--- test", - [] => "--- []", - :symbol => "--- !ruby/sym symbol", - {:a => "A"} => "--- \n !ruby/sym a: A" - }.each { |o,y| - it "should convert the #{o.class} #{o.inspect} to yaml" do - o.to_yaml.should == y - end - it "should produce yaml for the #{o.class} #{o.inspect} that can be reconstituted" do - YAML.load(o.to_yaml).should == o - end - } - # - # Can't test for equality on raw objects - { - Object.new => "--- !ruby/object {}", - [Object.new] => "--- \n - !ruby/object {}", - {Object.new => Object.new} => "--- \n ? !ruby/object {}\n : !ruby/object {}" - }.each { |o,y| - it "should convert the #{o.class} #{o.inspect} to yaml" do - o.to_yaml.should == y - end - it "should produce yaml for the #{o.class} #{o.inspect} that can be reconstituted" do - lambda { YAML.load(o.to_yaml) }.should_not raise_error - end - } -end - -- cgit