require "pact_broker/api/decorators/version_decorator"

module PactBroker
  module Api
    module Decorators
      describe VersionDecorator do
        describe "from_json" do
          let(:hash) do
            {
              buildUrl: "buildUrl",
              tags: [{ name: "main" }]
            }
          end

          subject { VersionDecorator.new(OpenStruct.new).from_json(hash.to_json) }

          it "sets the properties" do
            expect(subject.build_url).to eq "buildUrl"
            expect(subject.tags.first.name).to eq "main"
          end
        end

        describe "to_json" do
          before do
            allow(decorator).to receive(:deployed_versions_for_version_and_environment_url).and_return("http://deployed-versions")
            allow(decorator).to receive(:released_versions_for_version_and_environment_url).and_return("http://released-versions")
          end

          let(:version) do
            td.create_consumer("Consumer")
              .create_provider("providerA")
              .create_consumer_version("1.2.3", branch: "main")
              .create_consumer_version_tag("prod")
              .create_pact
              .create_provider("ProviderB")
              .create_pact
              .and_return(:consumer_version)
          end

          let(:environments) do
            [
              td.create_environment("test", uuid: "1234", display_name: "Test").and_return(:environment)
            ]
          end

          let(:deployed_versions) do
            [
              td.create_deployed_version_for_consumer_version(uuid: "1234", target: target, environment_name: "test").and_return(:deployed_version)
            ]
          end

          let(:target) { nil }

          let(:base_url) { "http://example.org" }
          let(:options) { { user_options: { base_url: base_url, resource_url: "resource_url", environments: environments, deployed_versions: deployed_versions } } }
          let(:decorator) { VersionDecorator.new(version) }

          subject { JSON.parse(decorator.to_json(options), symbolize_names: true) }

          it "includes a link to itself" do
            expect(subject[:_links][:self][:href]).to eq "resource_url"
          end

          it "includes the version number in the link" do
            expect(subject[:_links][:self][:name]).to eq "1.2.3"
          end

          it "includes its title in the link" do
            expect(subject[:_links][:self][:title]).to eq "Version"
          end

          it "includes the version number" do
            expect(subject[:number]).to eq "1.2.3"
          end

          it "includes a link to the pacticipant" do
            expect(subject[:_links][:'pb:pacticipant']).to eq title: "Pacticipant", name: "Consumer", href: "http://example.org/pacticipants/Consumer"
          end

          it "includes a link to get, create or delete a tag" do
            expect(subject[:_links][:'pb:tag']).to include href: "http://example.org/pacticipants/Consumer/versions/1.2.3/tags/{tag}", templated: true
          end

          it "includes a list of the tags" do
            expect(subject[:_embedded][:tags]).to be_instance_of(Array)
            expect(subject[:_embedded][:tags].first[:name]).to eq "prod"
          end

          it "includes the branch versions" do
            expect(subject[:_embedded][:branchVersions]).to be_instance_of(Array)
            expect(subject[:_embedded][:branchVersions].first[:name]).to eq "main"
            expect(subject[:_embedded][:branchVersions].first[:latest]).to eq true
          end

          it "includes the timestamps" do
            expect(subject[:createdAt]).to_not be nil
          end

          it "includes a list of sorted pacts" do
            expect(subject[:_links][:'pb:pact-versions']).to be_instance_of(Array)
            expect(subject[:_links][:'pb:pact-versions'].first[:href]).to include("1.2.3")
            expect(subject[:_links][:'pb:pact-versions'].first[:name]).to include("Pact between")
            expect(subject[:_links][:'pb:pact-versions'].first[:name]).to include("providerA")
            expect(subject[:_links][:'pb:pact-versions'].last[:name]).to include("ProviderB")
          end

          it "includes a link to the latest verification results for the pacts for this version" do
            expect(subject[:_links][:'pb:latest-verification-results-where-pacticipant-is-consumer'][:href]).to match(%r{http://.*/verification-results/.*/latest})
          end

          context "when application is deployed to an env" do
            it "includes a link to the deployed environments for this version" do
              expect(subject[:_links][:'pb:deployed-environments']).to be_instance_of(Array)
              expect(subject[:_links][:'pb:deployed-environments'].first).to eq(
                                                                               title: "Version deployed to Test",
                                                                               name: "Test",
                                                                               href: "http://example.org/deployed-versions/1234",
                                                                               currently_deployed: true,
                                                                               )
              expect(subject[:_links][:'pb:deployed-environments'].first).to_not include(:application_instance)
            end

            context "when deployed with application instance variable" do
              let(:target) { "instance-1" }

              it "includes a link to the deployed environments for this version along with application instance node" do
                expect(subject[:_links][:'pb:deployed-environments']).to be_instance_of(Array)
                expect(subject[:_links][:'pb:deployed-environments'].first).to eq(
                                                                                 title: "Version deployed to Test",
                                                                                 name: "Test",
                                                                                 href: "http://example.org/deployed-versions/1234",
                                                                                 currently_deployed: true,
                                                                                 application_instance: "instance-1",
                                                                                 )
              end
            end
          end

          it "includes a list of environments that this version can be deployed to" do
            expect(decorator).to receive(:deployed_versions_for_version_and_environment_url).with(version, environments.first, base_url)
            expect(subject[:_links][:'pb:record-deployment']).to be_instance_of(Array)
            expect(subject[:_links][:'pb:record-deployment'].first).to eq(
              name: "test",
              title: "Record deployment to Test",
              href: "http://deployed-versions"
            )
          end

          it "includes a list of environments that this version can be released to" do
            expect(decorator).to receive(:released_versions_for_version_and_environment_url).with(version, environments.first, base_url)
            expect(subject[:_links][:'pb:record-release']).to be_instance_of(Array)
            expect(subject[:_links][:'pb:record-release'].first).to eq(
              name: "test",
              title: "Record release to Test",
              href: "http://released-versions"
            )
          end

          context "when the environments option is not present" do
            let(:options) { { user_options: { base_url: base_url, resource_url: "resource_url" } } }

            it "does not include the pb:record-deployment or pb:record-release" do
              expect(subject[:_links]).to_not have_key(:'pb:record-deployment')
              expect(subject[:_links]).to_not have_key(:'pb:record-release')
            end
          end
        end
      end
    end
  end
end
