Please review pull request #435: Ticket/2.7.x/12349 module list dependencies opened by (mmrobins)

Description:

There was already a validate_dependencies method on the Puppet::Module
class, but it didn't do anything since it never actually read the
dependency info from the module metadata.

This commit adds a method to replace that, but it doesn't raise if
dependencies are unsatisfied. This allows us to output the dependency
problems when puppet module list is called.

This also adds basic acceptance tests for the list command.

  • Opened: Wed Feb 01 16:45:14 UTC 2012
  • Based on: puppetlabs:2.7.x (ec3bba5705378b92102adf4ce000ecfd42acf2f8)
  • Requested merge: mmrobins:ticket/2.7.x/12349-module_list_dependencies (a6c196f63be9164fbf515b37d382369be26a9a4b)

Diff follows:

diff --git a/acceptance/tests/modules/fake_modulepath/apache/metadata.json b/acceptance/tests/modules/fake_modulepath/apache/metadata.json
new file mode 100644
index 0000000..87fc9a7
--- /dev/null
+++ b/acceptance/tests/modules/fake_modulepath/apache/metadata.json
@@ -0,0 +1,54 @@
+{
+  "name": "puppetlabs-apache",
+  "dependencies": [
+
+  ],
+  "author": "",
+  "license": "",
+  "version": "0.0.3",
+  "checksums": {
+    "tests/ssl.pp": "191912535199531fd631f911c6329e56",
+    "manifests/params.pp": "8728cf041cdd94bb0899170eb2b417d9",
+    "tests/vhost.pp": "1b91e03c8ef89a7ecb6793831ac18399",
+    "manifests/php.pp": "8a5ca4035b1c22892923f3fde55e3d5e",
+    "lib/puppet/provider/a2mod/a2mod.rb": "18c5bb180b75a2375e95e07f88a94257",
+    "tests/php.pp": "ce7bb9eef69d32b42a32ce32d9653625",
+    "files/httpd": "295f5e924afe6f752d29327e73fe6d0a",
+    "manifests/dev.pp": "bc54a5af648cb04b7b3bb0e3f7be6543",
+    "manifests/ssl.pp": "11ed1861298c72cca3a706480bb0b67c",
+    "files/test.vhost": "0602022c19a7b6b289f218c7b93c1aea",
+    "tests/init.pp": "4eac4a7ef68499854c54a78879e25535",
+    "manifests/vhost.pp": "7806a6c098e217da046d0555314756c4",
+    "lib/puppet/type/a2mod.rb": "0e1b4843431413a10320ac1f6a055d15",
+    "templates/vhost-default.conf.erb": "ed64a53af0d7bad762176a98c9ea3e62",
+    "tests/dev.pp": "4cf15c1fecea3ca86009f182b402c7ab",
+    "tests/apache.pp": "4eac4a7ef68499854c54a78879e25535",
+    "Modulefile": "9b7a414bf15b06afe2f011068fcaff52",
+    "manifests/init.pp": "9ef7e081c832bca8f861c3a9feb9949d"
+  },
+  "types": [
+    {
+      "name": "a2mod",
+      "doc": "Manage Apache 2 modules on Debian and Ubuntu",
+      "parameters": [
+        {
+          "name": "name",
+          "doc": "The name of the module to be managed"
+        }
+      ],
+      "providers": [
+        {
+          "name": "a2mod",
+          "doc": "Manage Apache 2 modules on Debian and Ubuntu  Required binaries: ``a2enmod``, ``a2dismod``.    Default for ``operatingsystem`` == ``debianubuntu``.  "
+        }
+      ],
+      "properties": [
+        {
+          "name": "ensure",
+          "doc": "The basic property that the resource should be in.  Valid values are ``present``, ``absent``."
+        }
+      ]
+    }
+  ],
+  "source": ""
+}
\ No newline at end of file
diff --git a/acceptance/tests/modules/fake_modulepath/bacula/metadata.json b/acceptance/tests/modules/fake_modulepath/bacula/metadata.json
new file mode 100644
index 0000000..dbc7808
--- /dev/null
+++ b/acceptance/tests/modules/fake_modulepath/bacula/metadata.json
@@ -0,0 +1,53 @@
+{
+  "description": "This module manages Bacula, a complete backup solution",
+  "source": "http://github.com/puppetlabs/puppetlabs-bacula",
+  "checksums": {
+    "templates/client_config.erb": "5d4005eda5ace78fd90a8cb6dcb388bd",
+    "spec/spec_helper.rb": "ca19ec4f451ebc7fdb035b52eae6e909",
+    "templates/bacula-fd.conf.erb": "344fff616e138fbf8cd150f4bab8125d",
+    "CHANGELOG": "a20043858790de6086b9b30c36806cd2",
+    "manifests/config/validate.pp": "7d2f9f7cffb2bd1d221eb699ea55f246",
+    "LICENSE": "26ce13c80c7a8493533c65c32dc29f09",
+    "manifests/director.pp": "36f34f749314634173b6a47b92a58421",
+    "manifests/config/client.pp": "5d25cbfd94be2829d9d530673cd2307f",
+    "lib/puppet/parser/functions/generate_clients.rb": "e09d77f88d18ec3e59bbe7bae4a48036",
+    "manifests/common.pp": "67391e5560ee7fc97d873ab5c3e2f84c",
+    "manifests/client.pp": "42e9255711d0a6abed6ab8deafb1248c",
+    "manifests/console.pp": "4cf8ac2f96f9268e2a3d70ed777797b7",
+    "README.md": "13d0f1119d510e41c539cbc3825a4f82",
+    "templates/bacula-sd.conf.erb": "df7987695bf1dd18bb789a2673b2cd95",
+    "manifests/config.pp": "624a225f85f907e7db5cf717561953f3",
+    "tests/init.pp": "b95d199119d5e592df7e1580bf23fe06",
+    "templates/bconsole.conf.erb": "ba5e1ef7d320a48bde6e403dc956952a",
+    "manifests/bat.pp": "3743bddf0197b4110e54a8e297e927e8",
+    "manifests/storage.pp": "84f4ec1413df4fdec7288c18d6ba2897",
+    "templates/bacula-dir.conf.erb": "5ab5aa263cf318ebf65c6b52a0726647",
+    "metadata.json": "d34d0b70aba36510fbc2df4e667479ef",
+    "spec/spec.opts": "a600ded995d948e393fbe2320ba8e51c",
+    "Modulefile": "63f93af605871a69211cd3f79ac62582",
+    "manifests/init.pp": "fafac38161dd69f4b7e452cf910812f4"
+  },
+  "summary": "This module manages a bacula infrastructure",
+  "author": "Puppet Labs",
+  "dependencies": [
+    {
+      "version_requirement": ">= 2.2.0",
+      "name": "puppetlabs/stdlib"
+    },
+    {
+      "version_requirement": ">= 0.0.1",
+      "name": "puppetlabs/mysql"
+    },
+    {
+      "version_requirement": ">= 0.0.1",
+      "name": "puppetlabs/sqlite"
+    }
+  ],
+  "project_page": "http://github.com/puppetlabs/puppetlabs-bacula",
+  "types": [
+
+  ],
+  "license": "Apache",
+  "version": "0.0.2",
+  "name": "puppetlabs-bacula"
+}
\ No newline at end of file
diff --git a/acceptance/tests/modules/fake_modulepath/mysql/metadata.json b/acceptance/tests/modules/fake_modulepath/mysql/metadata.json
new file mode 100644
index 0000000..0aa833d
--- /dev/null
+++ b/acceptance/tests/modules/fake_modulepath/mysql/metadata.json
@@ -0,0 +1,140 @@
+{
+  "description": "Mysql module",
+  "source": "git://github.com/puppetlabs/puppetlabs-mysql.git",
+  "checksums": {
+    "lib/puppet/provider/database_grant/default.rb": "38a9c5fe0fe1b8474cc2bfd475a225f1",
+    "manifests/python.pp": "743e5ce2255afa9113a82c5e7fee3740",
+    "manifests/ruby.pp": "7b57a3321f90c455bccea9de1d57149a",
+    "manifests/params.pp": "9aeda052d3518d3fcd6e9ee353c899c5",
+    "lib/puppet/type/database_user.rb": "134269c960f9f751c33e0f023692e256",
+    "tests/mysql_user.pp": "7b066843d7cdcc54e95ae13ab82ec4f3",
+    "CHANGELOG": "f2e3e57948da2dcab3bdbe782efd6b11",
+    "lib/puppet/type/database.rb": "f6ca3a0d053c06752fec999a33c1f5a0",
+    "templates/my.cnf.erb": "302d55a6dfa368e3957abdd018e0c915",
+    "manifests/server.pp": "870e294ec504bde5174c203747312f8a",
+    "LICENSE": "0e5ccf641e613489e66aa98271dbe798",
+    "templates/my.cnf.pass.erb": "a4952e72bb8aea85a07274c2c1c0334f",
+    "manifests/server/mysqltuner.pp": "68951b161e11dfce8d93b202d7937704",
+    "manifests/server/monitor.pp": "76bb559e957086f6bd97ed286f15fd0c",
+    "lib/puppet/provider/database/mysql.rb": "92bd9124898e9a6258b585085034af4e",
+    "README": "33f2ef98ed5732170ea12de2598342a5",
+    "manifests/config.pp": "264b959f3529558050205eae26a61883",
+    "tests/python.pp": "b093828acfed9c14e25ebdd60d90c282",
+    "lib/puppet/provider/database/default.rb": "2f4d021abda21e363604403b0e0be231",
+    "lib/puppet/type/database_grant.rb": "d1b41c45e9c18262310b55170b364c75",
+    "files/mysqltuner.pl": "de535154b7fb28e437ba412434ea535e",
+    "tests/init.pp": "6b34827ac4731829c8a117f0b3fb8167",
+    "manifests/db.pp": "167ab5ec006ad0a9ea6d8a52f554eef5",
+    "TODO": "88ca4024a37992b46c34cb46e4ac39e6",
+    "tests/ruby.pp": "6c5071fcaf731995c9b8e31e00eaffa0",
+    "tests/mysql_database.pp": "2c611d35a1fabe5c418a917391dccade",
+    "lib/puppet/provider/database_grant/mysql.rb": "43cccef7eaf04b5cf343d2aff9147b99",
+    "tests/mysql_grant.pp": "106e1671b1f68701778401e4a3fc8d05",
+    "tests/server.pp": "afa67b373af325b705b49239c7e2efcf",
+    "lib/puppet/provider/database_user/mysql.rb": "5433dbcc8b596d6a141d0ee31e590f3e",
+    "lib/puppet/parser/functions/mysql_password.rb": "08aaa14cfbe99ceac1b59053685ee4c0",
+    "lib/puppet/provider/database_user/default.rb": "31cc564c11b58a23ab694ed17143f70f",
+    "Modulefile": "49f8c465c58c8841c2c1a98a8ad485dc",
+    "manifests/init.pp": "ed5175393dfa7da87e75a5f1ebfa21ef"
+  },
+  "summary": "Mysql module",
+  "author": "PuppetLabs",
+  "dependencies": [
+    {
+      "version_requirement": ">= 0.0.1",
+      "name": "bodepd/create_resources"
+    }
+  ],
+  "project_page": "http://github.com/puppetlabs/puppetlabs-mysql",
+  "types": [
+    {
+      "parameters": [
+        {
+          "doc": "The name of the database.",
+          "name": "name"
+        }
+      ],
+      "doc": "Manage creation/deletion of a database.",
+      "providers": [
+        {
+          "doc": "This is a default provider that does nothing. This allows us to install mysql on the same puppet run where we want to use it.    ",
+          "name": "default"
+        },
+        {
+          "doc": "Create mysql database.  Required binaries: `mysql`, `mysqladmin`, `mysqlshow`.    Default for `kernel` == `Linux`.  ",
+          "name": "mysql"
+        }
+      ],
+      "name": "database",
+      "properties": [
+        {
+          "doc": "The basic property that the resource should be in.  Valid values are `present`, `absent`.",
+          "name": "ensure"
+        },
+        {
+          "doc": "The characterset to use for a database  Values can match `/^\\S+$/`.",
+          "name": "charset"
+        }
+      ]
+    },
+    {
+      "parameters": [
+        {
+          "doc": "The primary key: either user@host for global privilges or user@host/database for database specific privileges",
+          "name": "name"
+        }
+      ],
+      "doc": "Manage a database user's rights.",
+      "providers": [
+        {
+          "doc": "Uses mysql as database.    ",
+          "name": "default"
+        },
+        {
+          "doc": "Uses mysql as database.  Required binaries: `mysql`, `mysqladmin`.    Default for `kernel` == `Linux`.  ",
+          "name": "mysql"
+        }
+      ],
+      "name": "database_grant",
+      "properties": [
+        {
+          "doc": "The privileges the user should have. The possible values are implementation dependent.",
+          "name": "privileges"
+        }
+      ]
+    },
+    {
+      "parameters": [
+        {
+          "doc": "The name of the user. This uses the 'username@hostname' or username@hosname.",
+          "name": "name"
+        }
+      ],
+      "doc": "Manage a database user. This includes management of users password as well as priveleges",
+      "providers": [
+        {
+          "doc": "manage users for a mysql database.    ",
+          "name": "default"
+        },
+        {
+          "doc": "manage users for a mysql database.  Required binaries: `mysql`, `mysqladmin`.    Default for `kernel` == `Linux`.  ",
+          "name": "mysql"
+        }
+      ],
+      "name": "database_user",
+      "properties": [
+        {
+          "doc": "The basic property that the resource should be in.  Valid values are `present`, `absent`.",
+          "name": "ensure"
+        },
+        {
+          "doc": "The password hash of the user. Use mysql_password() for creating such a hash.  Values can match `/\\w+/`.",
+          "name": "password_hash"
+        }
+      ]
+    }
+  ],
+  "license": "Apache",
+  "version": "0.0.0",
+  "name": "puppetlabs-mysql"
+}
diff --git a/acceptance/tests/modules/fake_modulepath/sqlite/metadata.json b/acceptance/tests/modules/fake_modulepath/sqlite/metadata.json
new file mode 100644
index 0000000..280db67
--- /dev/null
+++ b/acceptance/tests/modules/fake_modulepath/sqlite/metadata.json
@@ -0,0 +1,26 @@
+{
+  "description": "This module provides a sqlite class to manage\nthe installation of sqlite on a node.  It also provides\na sqlite::db defined type to manage databases on a system",
+  "source": "https://github.com/puppetlabs/puppetlabs-sqlite/",
+  "checksums": {
+    "spec/spec_helper.rb": "ca19ec4f451ebc7fdb035b52eae6e909",
+    "README.md": "ed04f8ed93d3a6ce19b9153b9444039c",
+    "tests/init.pp": "e8b321554c2d582e35beb01c57951062",
+    "manifests/db.pp": "ce94dbfcc3b10738eeec23304898ee78",
+    "metadata.json": "d34d0b70aba36510fbc2df4e667479ef",
+    "spec/spec.opts": "a600ded995d948e393fbe2320ba8e51c",
+    "Modulefile": "dda385f94c11e563df1fbe11eeba272d",
+    "manifests/init.pp": "859cb8ed63863adbaa202c45561280c5"
+  },
+  "summary": "Manage a sqlite installation and databases",
+  "author": "puppetlabs",
+  "dependencies": [
+
+  ],
+  "project_page": "http://projects.puppetlabs.com/projects/modules/issues",
+  "types": [
+
+  ],
+  "license": "Apache",
+  "version": "0.0.1.1",
+  "name": "puppetlabs-sqlite"
+}
diff --git a/acceptance/tests/modules/list.rb b/acceptance/tests/modules/list.rb
new file mode 100644
index 0000000..3cc2073
--- /dev/null
+++ b/acceptance/tests/modules/list.rb
@@ -0,0 +1,26 @@
+test_name "puppet module list test output and dependency error checking"
+
+step "Run puppet module list"
+expected_stdout = <<-HEREDOC
+/opt/puppet-git-repos/puppet/acceptance/tests/modules/fake_modulepath
+  mysql (0.0.0)
+  apache (0.0.3)
+  bacula (0.0.2)
+  sqlite (0.0.1.1)
+  HEREDOC
+
+expected_stderr = <<-HEREDOC
+Missing dependency `create_resources`:
+  `mysql` (0.0.0) requires `bodepd/create_resources` (>= 0.0.1)
+Missing dependency `stdlib`:
+  `bacula` (0.0.2) requires `puppetlabs/stdlib` (>= 2.2.0)
+Version dependency mismatch `mysql` (0.0.0):
+  `bacula` (0.0.2) requires `puppetlabs/mysql` (>= 0.0.1)
+Non semantic version dependency `sqlite` (0.0.1.1):
+  `bacula` (0.0.2) requires `puppetlabs/sqlite` (>= 0.0.1)
+  HEREDOC
+
+on master, "puppet module list --modulepath /opt/puppet-git-repos/puppet/acceptance/tests/modules/fake_modulepath" do
+  assert_match(expected_stdout, stdout, "puppet module list did not output expected stdout")
+  assert_match(expected_stderr, stderr, "puppet module list did not output expected stderr")
+end
diff --git a/lib/puppet/face/module/list.rb b/lib/puppet/face/module/list.rb
index 7729900..fae4060 100644
--- a/lib/puppet/face/module/list.rb
+++ b/lib/puppet/face/module/list.rb
@@ -48,8 +48,23 @@
       environment.modules_by_path
     end
 
-    when_rendering :console do |modules_by_path|
+    when_rendering :console do |modules_by_path, options|
       output = ''
+
+      Puppet[:modulepath] = options[:modulepath] if options[:modulepath]
+      environment = Puppet::Node::Environment.new(options[:env])
+
+      dependency_errors = false
+
+      environment.modules.each do |mod|
+        mod.unsatisfied_dependencies.each do |dep_issue|
+          dependency_errors = true
+          $stderr.puts dep_issue.to_s
+        end
+      end
+
+      output << "\n" if dependency_errors
+
       modules_by_path.each do |path, modules|
         output << "#{path}\n"
         modules.each do |mod|
diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb
index 7f86df8..5d34ae9 100644
--- a/lib/puppet/module.rb
+++ b/lib/puppet/module.rb
@@ -1,4 +1,5 @@
 require 'puppet/util/logging'
+require 'semver'
 
 # Support for modules
 class Puppet::Module
@@ -30,6 +31,7 @@ def self.find(modname, environment = nil)
   attr_reader :name, :environment
   attr_writer :environment
 
+  attr_accessor :dependencies
   attr_accessor :source, :author, :version, :license, :puppetversion, :summary, :description, :project_page
 
   def has_metadata?
@@ -56,7 +58,6 @@ def initialize(name, options = {})
     load_metadata if has_metadata?
 
     validate_puppet_version
-    validate_dependencies
   end
 
   FILETYPES.each do |type|
@@ -107,7 +108,7 @@ def license_file
 
   def load_metadata
     data = "" File.read(metadata_file)
-    [:source, :author, :version, :license, :puppetversion].each do |attr|
+    [:source, :author, :version, :license, :puppetversion, :dependencies].each do |attr|
       unless value = data[attr.to_s]
         unless attr == :puppetversion
           raise MissingMetadata, "No #{attr} module metadata provided for #{self.name}"
@@ -154,18 +155,52 @@ def to_s
     result
   end
 
-  def validate_dependencies
-    return unless defined?(@requires)
+  def unsatisfied_dependencies
+    return [] unless dependencies
 
-    @requires.each do |name, version|
-      unless mod = environment.module(name)
-        raise MissingModule, "Missing module #{name} required by #{self.name}"
+    unsatisfied_dependencies = []
+
+    dependencies.each do |dependency|
+      forge_name = dependency['name']
+      author, dep_name = forge_name.split('/')
+      version_string = dependency['version_requirement']
+
+      equality, dep_version = version_string ? version_string.split("\s") : [nil, nil]
+
+      unless dep_mod = environment.module(dep_name)
+        msg =  "Missing dependency `#{dep_name}`:\n"
+        msg += "  `#{self.name}` (#{self.version}) requires `#{forge_name}` (#{version_string})\n"
+        unsatisfied_dependencies << msg
+        next
+      end
+
+      if dep_version && !dep_mod.version
+        msg =  "Unversioned dependency `#{dep_mod.name}`:\n"
+        msg += "  `#{self.name}` (#{self.version}) requires `#{forge_name}` (#{version_string})\n"
+        unsatisfied_dependencies << msg
+        next
       end
 
-      if version and mod.version != version
-        raise IncompatibleModule, "Required module #{name} is version #{mod.version} but #{self.name} requires #{version}"
+      if dep_version
+        begin
+          required_version_semver = SemVer.new(dep_version)
+          actual_version_semver = SemVer.new(dep_mod.version)
+        rescue ArgumentError
+          msg =  "Non semantic version dependency `#{dep_mod.name}` (#{dep_mod.version}):\n"
+          msg += "  `#{self.name}` (#{self.version}) requires `#{forge_name}` (#{version_string})\n"
+          unsatisfied_dependencies << msg
+          next
+        end
+
+        if !actual_version_semver.send(equality, required_version_semver)
+          msg =  "Version dependency mismatch `#{dep_mod.name}` (#{dep_mod.version}):\n"
+          msg += "  `#{self.name}` (#{self.version}) requires `#{forge_name}` (#{version_string})\n"
+          unsatisfied_dependencies << msg
+          next
+        end
       end
     end
+    unsatisfied_dependencies
   end
 
   def validate_puppet_version
@@ -191,7 +226,7 @@ def backward_compatible_plugins_dir
   end
 
   def assert_validity
-    raise InvalidName, "Invalid module name; module names must be alphanumeric (plus '-'), not '#{name}'" unless name =~ /^[-\w]+$/
+    raise InvalidName, "Invalid module name #{name}; module names must be alphanumeric (plus '-'), not '#{name}'" unless name =~ /^[-\w]+$/
   end
 
   def ==(other)
diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb
index e117249..0b6ae56 100755
--- a/spec/unit/module_spec.rb
+++ b/spec/unit/module_spec.rb
@@ -89,6 +89,141 @@
     lambda { mod.validate_puppet_version }.should raise_error(Puppet::Module::IncompatibleModule)
   end
 
+  describe "when finding unsatisfied dependencies" do
+    before do
+      @mod = Puppet::Module.new("mymod")
+      @mod.stubs(:dependencies).returns [
+        {
+          "version_requirement" => ">= 2.2.0",
+          "name"                => "baz/foobar"
+        }
+      ]
+    end
+
+    it "should list modules that are missing" do
+      @mod.unsatisfied_dependencies.should == [<<-HEREDOC.gsub(/^\s{10}/, '')
+          Missing dependency `foobar`:
+            `mymod` () requires `baz/foobar` (>= 2.2.0)
+        HEREDOC
+      ]
+    end
+
+    it "should list modules with unsatisfied version" do
+      foobar = Puppet::Module.new("foobar")
+      foobar.version = '2.0.0'
+      @mod.environment.expects(:module).with("foobar").returns foobar
+
+      @mod.unsatisfied_dependencies.should == [<<-HEREDOC.gsub(/^\s{10}/, '')
+          Version dependency mismatch `foobar` (2.0.0):
+            `mymod` () requires `baz/foobar` (>= 2.2.0)
+        HEREDOC
+      ]
+    end
+
+    it "should consider a dependency without a version requirement to be satisfied" do
+      mod = Puppet::Module.new("mymod")
+      mod.stubs(:dependencies).returns [{ "name" => "baz/foobar" }]
+
+      foobar = Puppet::Module.new("foobar")
+      mod.environment.expects(:module).with("foobar").returns foobar
+
+      mod.unsatisfied_dependencies.should be_empty
+    end
+
+    it "should consider a dependency without a version to be unsatisfied" do
+      foobar = Puppet::Module.new("foobar")
+      @mod.environment.expects(:module).with("foobar").returns foobar
+
+      @mod.unsatisfied_dependencies.should == [<<-HEREDOC.gsub(/^\s{10}/, '')
+          Unversioned dependency `foobar`:
+            `mymod` () requires `baz/foobar` (>= 2.2.0)
+        HEREDOC
+      ]
+    end
+
+    it "should consider a dependency without a semantic version to be unsatisfied" do
+      foobar = Puppet::Module.new("foobar")
+      foobar.version = '5.1'
+      @mod.environment.expects(:module).with("foobar").returns foobar
+
+      @mod.unsatisfied_dependencies.should == [<<-HEREDOC.gsub(/^\s{10}/, '')
+          Non semantic version dependency `foobar` (5.1):
+            `mymod` () requires `baz/foobar` (>= 2.2.0)
+        HEREDOC
+      ]
+    end
+
+    it "should consider a dependency requirement without a semantic version to be unsatisfied" do
+      foobar = Puppet::Module.new("foobar")
+      foobar.version = '5.1.0'
+
+      mod = Puppet::Module.new("mymod")
+      mod.stubs(:dependencies).returns [{ "name" => "baz/foobar", "version_requirement" => '> 2.0' }]
+      mod.environment.expects(:module).with("foobar").returns foobar
+
+      mod.unsatisfied_dependencies.should == [<<-HEREDOC.gsub(/^\s{10}/, '')
+          Non semantic version dependency `foobar` (5.1.0):
+            `mymod` () requires `baz/foobar` (> 2.0)
+        HEREDOC
+      ]
+    end
+
+    it "should have valid dependencies when no dependencies have been specified" do
+      mod = Puppet::Module.new("mymod")
+
+      mod.unsatisfied_dependencies.should == []
+    end
+
+    it "should only list unsatisfied dependencies" do
+      mod = Puppet::Module.new("mymod")
+      mod.stubs(:dependencies).returns [
+        {
+          "version_requirement" => ">= 2.2.0",
+          "name"                => "baz/satisfied"
+        },
+        {
+          "version_requirement" => ">= 2.2.0",
+          "name"                => "baz/notsatisfied"
+        }
+      ]
+
+      satisfied = Puppet::Module.new("satisfied")
+      satisfied.version = "3.3.0"
+
+      mod.environment.expects(:module).with("satisfied").returns satisfied
+      mod.environment.expects(:module).with("notsatisfied").returns nil
+
+      mod.unsatisfied_dependencies.should == [<<-HEREDOC.gsub(/^\s{10}/, '')
+          Missing dependency `notsatisfied`:
+            `mymod` () requires `baz/notsatisfied` (>= 2.2.0)
+        HEREDOC
+      ]
+    end
+
+    it "should be empty when all dependencies are met" do
+      mod = Puppet::Module.new("mymod")
+      mod.stubs(:dependencies).returns [
+        {
+          "version_requirement" => ">= 2.2.0",
+          "name"                => "baz/satisfied"
+        },
+        {
+          "version_requirement" => "< 2.2.0",
+          "name"                => "baz/alsosatisfied"
+        }
+      ]
+      satisfied = Puppet::Module.new("satisfied")
+      satisfied.version = "3.3.0"
+      alsosatisfied = Puppet::Module.new("alsosatisfied")
+      alsosatisfied.version = "2.1.0"
+
+      mod.environment.expects(:module).with("satisfied").returns satisfied
+      mod.environment.expects(:module).with("alsosatisfied").returns alsosatisfied
+
+      mod.unsatisfied_dependencies.should be_empty
+    end
+  end
+
   describe "when managing supported platforms" do
     it "should support specifying a supported platform" do
       mod = Puppet::Module.new("mymod")
@@ -469,14 +604,15 @@
     Puppet::Module.new("yay")
   end
 
-  describe "when loading the medatada file", :if => Puppet.features.pson? do
+  describe "when loading the metadata file", :if => Puppet.features.pson? do
     before do
       @data = {
         :license       => "GPL2",
         :author        => "luke",
         :version       => "1.0",
         :source        => "http://foo/",
-        :puppetversion => "0.25"
+        :puppetversion => "0.25",
+        :dependencies  => []
       }
       @text = @data.to_pson
 

    

--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To post to this group, send email to puppet-dev@googlegroups.com.
To unsubscribe from this group, send email to puppet-dev+unsubscr...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to