#!/usr/bin/env rspec
require 'spec_helper'

describe Puppet::Parser::AST::Resource do
  ast = Puppet::Parser::AST

  describe "for builtin types" do
    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)
      @instance = ast::ResourceInstance.new(:title => @title, :parameters => ast::ASTArray.new(:children => []))
      @resource = ast::Resource.new(:type => "file", :instances => ast::ASTArray.new(:children => [@instance]))
      @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"))
      @instance.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)

      @instance.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)

      @instance.title = array
      result = @resource.evaluate(@scope).collect { |r| r.title }
      result.should be_include("one")
      result.should be_include("two")
    end

    it "should implicitly iterate over instances" do
      new_title = Puppet::Parser::AST::String.new(:value => "other_title")
      new_instance = ast::ResourceInstance.new(:title => new_title, :parameters => ast::ASTArray.new(:children => []))
      @resource.instances.push(new_instance)
      @resource.evaluate(@scope).collect { |r| r.title }.should == ["mytitle", "other_title"]
    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)

      @instance.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)
        ["one", "one::two", "three"].each do |name|
          @parser.environment.known_resource_types.add(Puppet::Resource::Type.new(:definition, name, {}))
        end
        @twoscope = @scope.newscope(:namespace => "one")
        @twoscope.resource = @scope.resource
      end

      def resource(type, params = nil)
        params ||= Puppet::Parser::AST::ASTArray.new(:children => [])
        instance = Puppet::Parser::AST::ResourceInstance.new(
                                                             :title => Puppet::Parser::AST::String.new(:value => "myresource"), :parameters => params)
        Puppet::Parser::AST::Resource.new(:type => type,
                                          :instances => Puppet::Parser::AST::ASTArray.new(:children => [instance]))
      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 correctly generate resources that can look up defined classes by title" do
        @scope.known_resource_types.add_hostclass Puppet::Resource::Type.new(:hostclass, "Myresource", {})
        @scope.compiler.stubs(:evaluate_classes)
        res = resource("class").evaluate(@twoscope)[0]
        res.type.should == "Class"
        res.title.should == "Myresource"
      end

      it "should evaluate parameterized classes when they are instantiated" do
        @scope.known_resource_types.add_hostclass Puppet::Resource::Type.new(:hostclass, "Myresource", {})
        @scope.compiler.expects(:evaluate_classes).with(['myresource'],@twoscope,false)
        resource("class").evaluate(@twoscope)[0]
      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

  describe "for class resources" do
    before do
      @title = Puppet::Parser::AST::String.new(:value => "classname")
      @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode"))
      @scope = Puppet::Parser::Scope.new(:compiler => @compiler)
      @scope.stubs(:resource).returns(stub_everything)
      @instance = ast::ResourceInstance.new(:title => @title, :parameters => ast::ASTArray.new(:children => []))
      @resource = ast::Resource.new(:type => "Class", :instances => ast::ASTArray.new(:children => [@instance]))
      @resource.stubs(:qualified_type).returns("Resource")
      @type = Puppet::Resource::Type.new(:hostclass, "classname")
      @compiler.known_resource_types.add(@type)
    end

    it "should instantiate the class" do
      @compiler.stubs(:evaluate_classes)
      result = @resource.evaluate(@scope)
      result.length.should == 1
      result.first.ref.should == "Class[Classname]"
      @compiler.catalog.resource("Class[Classname]").should equal(result.first)
    end

    it "should cause its parent to be evaluated" do
      parent_type = Puppet::Resource::Type.new(:hostclass, "parentname")
      @compiler.stubs(:evaluate_classes)
      @compiler.known_resource_types.add(parent_type)
      @type.parent = "parentname"
      result = @resource.evaluate(@scope)
      result.length.should == 1
      result.first.ref.should == "Class[Classname]"
      @compiler.catalog.resource("Class[Classname]").should equal(result.first)
      @compiler.catalog.resource("Class[Parentname]").should be_instance_of(Puppet::Parser::Resource)
    end

  end

end
