# Copyright 2015 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require "helper"

describe Google::Cloud::Dns::Project, :mock_dns do
  it "knows the project identifier" do
    _(dns.project).must_equal project
  end

  it "knows its quota information" do
    mock = Minitest::Mock.new
    mock.expect :get_project, random_project_gapi, [project]

    dns.service.mocked_service = mock
    dns.reload!
    mock.verify

    _(dns.id).must_equal project
    _(dns.number).must_equal 123456789
    _(dns.zones_quota).must_equal 101
    _(dns.records_per_zone).must_equal 1002
    _(dns.additions_per_change).must_equal 103
    _(dns.deletions_per_change).must_equal 104
    _(dns.total_data_per_change).must_equal 8000
    _(dns.data_per_record).must_equal 105
  end

  it "finds a zone" do
    found_zone = "example.net."
    mock = Minitest::Mock.new
    mock.expect :get_managed_zone, random_zone_gapi(found_zone, nil), [project, found_zone]

    dns.service.mocked_service = mock
    zone = dns.zone found_zone
    mock.verify

    _(zone).must_be_kind_of Google::Cloud::Dns::Zone
    _(zone.name).must_equal found_zone
  end

  it "returns nil when it cannot find a zone" do
    stub = Object.new
    def stub.get_managed_zone *args
      raise Google::Apis::ClientError.new nil, status_code: 404
    end

    dns.service.mocked_service = stub
    zone = dns.zone "example.org."
    _(zone).must_be :nil?
  end

  it "lists zones" do
    num_zones = 3
    mock = Minitest::Mock.new
    mock.expect :list_managed_zones, list_zones_gapi(num_zones), [project], max_results: nil, page_token: nil

    dns.service.mocked_service = mock
    zones = dns.zones
    mock.verify

    _(zones.size).must_equal num_zones
    zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
  end

  it "paginates zones" do
    mock = Minitest::Mock.new
    mock.expect :list_managed_zones, list_zones_gapi(3, "next_page_token"), [project], max_results: nil, page_token: nil
    mock.expect :list_managed_zones, list_zones_gapi(2), [project], max_results: nil, page_token: "next_page_token"

    dns.service.mocked_service = mock
    first_zones = dns.zones
    _(first_zones.count).must_equal 3
    first_zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
    _(first_zones.token).wont_be :nil?
    _(first_zones.token).must_equal "next_page_token"

    second_zones = dns.zones token: first_zones.token
    mock.verify

    _(second_zones.count).must_equal 2
    second_zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
    _(second_zones.token).must_be :nil?
  end

  it "paginates zones with max set" do
    mock = Minitest::Mock.new
    mock.expect :list_managed_zones, list_zones_gapi(3, "next_page_token"), [project], max_results: 3, page_token: nil

    dns.service.mocked_service = mock
    zones = dns.zones max: 3
    mock.verify

    _(zones.count).must_equal 3
    zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
    _(zones.token).wont_be :nil?
    _(zones.token).must_equal "next_page_token"
  end

  it "paginates zones without max set" do
    mock = Minitest::Mock.new
    mock.expect :list_managed_zones, list_zones_gapi(3, "next_page_token"), [project], max_results: nil, page_token: nil

    dns.service.mocked_service = mock
    zones = dns.zones
    mock.verify

    _(zones.count).must_equal 3
    zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
    _(zones.token).wont_be :nil?
    _(zones.token).must_equal "next_page_token"
  end

  it "paginates zones with next? and next" do
    mock = Minitest::Mock.new
    mock.expect :list_managed_zones, list_zones_gapi(3, "next_page_token"), [project], max_results: nil, page_token: nil
    mock.expect :list_managed_zones, list_zones_gapi(2), [project], max_results: nil, page_token: "next_page_token"

    dns.service.mocked_service = mock
    first_zones = dns.zones
    _(first_zones.count).must_equal 3
    first_zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
    _(first_zones.next?).must_equal true

    second_zones = first_zones.next
    mock.verify

    _(second_zones.count).must_equal 2
    second_zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
    _(second_zones.next?).must_equal false
  end

  it "paginates zones with next? and next and max set" do
    mock = Minitest::Mock.new
    mock.expect :list_managed_zones, list_zones_gapi(3, "next_page_token"), [project], max_results: 3, page_token: nil
    mock.expect :list_managed_zones, list_zones_gapi(2), [project], max_results: 3, page_token: "next_page_token"

    dns.service.mocked_service = mock
    first_zones = dns.zones max: 3
    _(first_zones.count).must_equal 3
    first_zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
    _(first_zones.next?).must_equal true

    second_zones = first_zones.next
    mock.verify

    _(second_zones.count).must_equal 2
    second_zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
    _(second_zones.next?).must_equal false
  end

  it "paginates zones with all" do
    mock = Minitest::Mock.new
    mock.expect :list_managed_zones, list_zones_gapi(3, "next_page_token"), [project], max_results: nil, page_token: nil
    mock.expect :list_managed_zones, list_zones_gapi(2), [project], max_results: nil, page_token: "next_page_token"

    dns.service.mocked_service = mock
    zones = dns.zones.all.to_a
    mock.verify

    _(zones.count).must_equal 5
    zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
  end

  it "paginates zones with all and max set" do
    mock = Minitest::Mock.new
    mock.expect :list_managed_zones, list_zones_gapi(3, "next_page_token"), [project], max_results: 3, page_token: nil
    mock.expect :list_managed_zones, list_zones_gapi(2), [project], max_results: 3, page_token: "next_page_token"

    dns.service.mocked_service = mock
    zones = dns.zones(max: 3).all.to_a
    mock.verify

    _(zones.count).must_equal 5
    zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
  end

  it "iterates all zones with all using Enumerator" do
    mock = Minitest::Mock.new
    mock.expect :list_managed_zones, list_zones_gapi(3, "next_page_token"), [project], max_results: nil, page_token: nil
    mock.expect :list_managed_zones, list_zones_gapi(3, "second_page_token"), [project], max_results: nil, page_token: "next_page_token"

    dns.service.mocked_service = mock
    zones = dns.zones.all.take(5)
    mock.verify

    _(zones.count).must_equal 5
    zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
  end

  it "iterates all zones with all with request_limit set" do
    mock = Minitest::Mock.new
    mock.expect :list_managed_zones, list_zones_gapi(3, "next_page_token"), [project], max_results: nil, page_token: nil
    mock.expect :list_managed_zones, list_zones_gapi(3, "second_page_token"), [project], max_results: nil, page_token: "next_page_token"

    dns.service.mocked_service = mock
    zones = dns.zones.all(request_limit: 1).to_a
    mock.verify

    _(zones.count).must_equal 6
    zones.each { |z| _(z).must_be_kind_of Google::Cloud::Dns::Zone }
  end

  it "creates a zone" do
    mock = Minitest::Mock.new
    managed_zone_gapi = create_zone_gapi "example-zone", "example.net."
    mock.expect :create_managed_zone, managed_zone_gapi, [project, managed_zone_gapi]

    dns.service.mocked_service = mock
    zone = dns.create_zone "example-zone", "example.net."
    mock.verify

    _(zone).must_be_kind_of Google::Cloud::Dns::Zone
    _(zone.name).must_equal "example-zone"
    _(zone.dns).must_equal "example.net."
    _(zone.description).must_equal ""
    _(zone.name_server_set).must_be :nil?
  end

  it "creates a zone with a description" do
    mock = Minitest::Mock.new
    managed_zone_gapi = create_zone_gapi "example-zone", "example.net.", description: "Example Zone Description"
    mock.expect :create_managed_zone, managed_zone_gapi, [project, managed_zone_gapi]

    dns.service.mocked_service = mock
    zone = dns.create_zone "example-zone", "example.net.",
                            description: "Example Zone Description"
    mock.verify

    _(zone).must_be_kind_of Google::Cloud::Dns::Zone
    _(zone.name).must_equal "example-zone"
    _(zone.dns).must_equal "example.net."
    _(zone.description).must_equal "Example Zone Description"
    _(zone.name_server_set).must_be :nil?
  end

  it "creates a zone with a name_server_set" do
    mock = Minitest::Mock.new
    managed_zone_gapi = create_zone_gapi "example-zone", "example.net.", name_server_set: "example-set"
    mock.expect :create_managed_zone, managed_zone_gapi, [project, managed_zone_gapi]

    dns.service.mocked_service = mock
    zone = dns.create_zone "example-zone", "example.net.",
                            name_server_set: "example-set"
    mock.verify

    _(zone).must_be_kind_of Google::Cloud::Dns::Zone
    _(zone.name).must_equal "example-zone"
    _(zone.dns).must_equal "example.net."
    _(zone.description).must_equal ""
    _(zone.name_server_set).must_equal "example-set"
  end

  it "reload! calls to the API" do
    mock = Minitest::Mock.new
    mock.expect :get_project, random_project_gapi, [project]

    dns.service.mocked_service = mock
    dns.reload!
    mock.verify
  end

  def list_zones_gapi count = 2, token = nil
    zones = count.times.map do
      seed = rand 99999
      random_zone_gapi "example-#{seed}-zone", "example-#{seed}.com."
    end
    Google::Apis::DnsV1::ListManagedZonesResponse.new(
      kind: "dns#managedZonesListResponse",
      managed_zones: zones,
      next_page_token: token
    )
  end

  def create_zone_gapi name, dns, options = {}
    Google::Apis::DnsV1::ManagedZone.new(
      kind: "dns#managedZone",
      name: name,
      dns_name: dns,
      description: (options[:description] || ""),
      name_server_set: options[:name_server_set]
    )
  end
end
