require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20171124104327_migrate_kubernetes_service_to_new_clusters_architectures.rb')

describe MigrateKubernetesServiceToNewClustersArchitectures, :migration do
  context 'when unique KubernetesService exists' do
    shared_examples 'KubernetesService migration' do
      let(:sample_num) { 2 }

      let(:projects) do
        (1..sample_num).each_with_object([]) do |n, array|
          array << MigrateKubernetesServiceToNewClustersArchitectures::Project.create!
        end
      end

      let!(:kubernetes_services) do
        projects.map do |project|
          MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
            project: project,
            active: active,
            category: 'deployment',
            type: 'KubernetesService',
            properties: "{\"namespace\":\"prod\",\"api_url\":\"https://kubernetes#{project.id}.com\",\"ca_pem\":\"ca_pem#{project.id}\",\"token\":\"token#{project.id}\"}")
        end
      end

      it 'migrates the KubernetesService to Platform::Kubernetes' do
        expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(sample_num)

        projects.each do |project|
          project.clusters.last.tap do |cluster|
            expect(cluster.enabled).to eq(active)
            expect(cluster.platform_kubernetes.api_url).to eq(project.kubernetes_service.api_url)
            expect(cluster.platform_kubernetes.ca_cert).to eq(project.kubernetes_service.ca_pem)
            expect(cluster.platform_kubernetes.token).to eq(project.kubernetes_service.token)
            expect(project.kubernetes_service).not_to be_active
          end
        end
      end
    end

    context 'when KubernetesService is active' do
      let(:active) { true }

      it_behaves_like 'KubernetesService migration'
    end
  end

  context 'when unique KubernetesService spawned from Service Template' do
    let(:sample_num) { 2 }

    let(:projects) do
      (1..sample_num).each_with_object([]) do |n, array|
        array << MigrateKubernetesServiceToNewClustersArchitectures::Project.create!
      end
    end

    let!(:kubernetes_service_template) do
      MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
        template: true,
        category: 'deployment',
        type: 'KubernetesService',
        properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}")
    end

    let!(:kubernetes_services) do
      projects.map do |project|
        MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
          project: project,
          category: 'deployment',
          type: 'KubernetesService',
          properties: "{\"namespace\":\"prod\",\"api_url\":\"#{kubernetes_service_template.api_url}\",\"ca_pem\":\"#{kubernetes_service_template.ca_pem}\",\"token\":\"#{kubernetes_service_template.token}\"}")
      end
    end

    it 'migrates the KubernetesService to Platform::Kubernetes without template' do
      expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(sample_num)

      projects.each do |project|
        project.clusters.last.tap do |cluster|
          expect(cluster.platform_kubernetes.api_url).to eq(project.kubernetes_service.api_url)
          expect(cluster.platform_kubernetes.ca_cert).to eq(project.kubernetes_service.ca_pem)
          expect(cluster.platform_kubernetes.token).to eq(project.kubernetes_service.token)
          expect(project.kubernetes_service).not_to be_active
        end
      end
    end
  end

  context 'when managed KubernetesService exists' do
    let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }

    let(:cluster) do
      MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
        projects: [project],
        name: 'sample-cluster',
        platform_type: :kubernetes,
        provider_type: :user,
        platform_kubernetes_attributes: {
          api_url: 'https://sample.kubernetes.com',
          ca_cert: 'ca_pem-sample',
          token: 'token-sample'
        } )
    end

    let!(:kubernetes_service) do
      MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
        project: project,
        active: cluster.enabled,
        category: 'deployment',
        type: 'KubernetesService',
        properties: "{\"api_url\":\"#{cluster.platform_kubernetes.api_url}\"}")
    end

    it 'does not migrate the KubernetesService and disables the kubernetes_service' do # Because the corresponding Platform::Kubernetes already exists
      expect { migrate! }.not_to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }

      kubernetes_service.reload
      expect(kubernetes_service).not_to be_active
    end
  end

  context 'when production cluster has already been existed' do # i.e. There are no environment_scope conflicts
    let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }

    let(:cluster) do
      MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
        projects: [project],
        name: 'sample-cluster',
        platform_type: :kubernetes,
        provider_type: :user,
        environment_scope: 'production/*',
        platform_kubernetes_attributes: {
          api_url: 'https://sample.kubernetes.com',
          ca_cert: 'ca_pem-sample',
          token: 'token-sample'
        } )
    end

    let!(:kubernetes_service) do
      MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
        project: project,
        active: true,
        category: 'deployment',
        type: 'KubernetesService',
        properties: "{\"api_url\":\"https://debug.kube.com\"}")
    end

    it 'migrates the KubernetesService to Platform::Kubernetes' do
      expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)

      kubernetes_service.reload
      project.clusters.last.tap do |cluster|
        expect(cluster.environment_scope).to eq('*')
        expect(cluster.platform_kubernetes.api_url).to eq(kubernetes_service.api_url)
        expect(cluster.platform_kubernetes.ca_cert).to eq(kubernetes_service.ca_pem)
        expect(cluster.platform_kubernetes.token).to eq(kubernetes_service.token)
        expect(kubernetes_service).not_to be_active
      end
    end
  end

  context 'when default cluster has already been existed' do
    let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }

    let!(:cluster) do
      MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
        projects: [project],
        name: 'sample-cluster',
        platform_type: :kubernetes,
        provider_type: :user,
        environment_scope: '*',
        platform_kubernetes_attributes: {
          api_url: 'https://sample.kubernetes.com',
          ca_cert: 'ca_pem-sample',
          token: 'token-sample'
        } )
    end

    let!(:kubernetes_service) do
      MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
        project: project,
        active: true,
        category: 'deployment',
        type: 'KubernetesService',
        properties: "{\"api_url\":\"https://debug.kube.com\"}")
    end

    it 'migrates the KubernetesService to Platform::Kubernetes with dedicated environment_scope' do # Because environment_scope is duplicated
      expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)

      kubernetes_service.reload
      project.clusters.last.tap do |cluster|
        expect(cluster.environment_scope).to eq('migrated/*')
        expect(cluster.platform_kubernetes.api_url).to eq(kubernetes_service.api_url)
        expect(cluster.platform_kubernetes.ca_cert).to eq(kubernetes_service.ca_pem)
        expect(cluster.platform_kubernetes.token).to eq(kubernetes_service.token)
        expect(kubernetes_service).not_to be_active
      end
    end
  end

  context 'when default cluster and migrated cluster has already been existed' do
    let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }

    let!(:cluster) do
      MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
        projects: [project],
        name: 'sample-cluster',
        platform_type: :kubernetes,
        provider_type: :user,
        environment_scope: '*',
        platform_kubernetes_attributes: {
          api_url: 'https://sample.kubernetes.com',
          ca_cert: 'ca_pem-sample',
          token: 'token-sample'
        } )
    end

    let!(:migrated_cluster) do
      MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
        projects: [project],
        name: 'sample-cluster',
        platform_type: :kubernetes,
        provider_type: :user,
        environment_scope: 'migrated/*',
        platform_kubernetes_attributes: {
          api_url: 'https://sample.kubernetes.com',
          ca_cert: 'ca_pem-sample',
          token: 'token-sample'
        } )
    end

    let!(:kubernetes_service) do
      MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
        project: project,
        active: true,
        category: 'deployment',
        type: 'KubernetesService',
        properties: "{\"api_url\":\"https://debug.kube.com\"}")
    end

    it 'migrates the KubernetesService to Platform::Kubernetes with dedicated environment_scope' do # Because environment_scope is duplicated
      expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)

      kubernetes_service.reload
      project.clusters.last.tap do |cluster|
        expect(cluster.environment_scope).to eq('migrated0/*')
        expect(cluster.platform_kubernetes.api_url).to eq(kubernetes_service.api_url)
        expect(cluster.platform_kubernetes.ca_cert).to eq(kubernetes_service.ca_pem)
        expect(cluster.platform_kubernetes.token).to eq(kubernetes_service.token)
        expect(kubernetes_service).not_to be_active
      end
    end
  end

  context 'when KubernetesService has nullified parameters' do
    let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }

    before do
      MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
        project: project,
        active: false,
        category: 'deployment',
        type: 'KubernetesService',
        properties: "{}")
    end

    it 'does not migrate the KubernetesService and disables the kubernetes_service' do
      expect { migrate! }.not_to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }

      expect(project.kubernetes_service).not_to be_active
    end
  end

  # Platforms::Kubernetes validates `token` reagdless of the activeness,
  # whereas KubernetesService validates `token` if only it's activated
  # However, in this migration file, there are no validations because of the re-defined model class
  # therefore, we should safely add this raw to Platform::Kubernetes
  context 'when KubernetesService has empty token' do
    let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }

    before do
      MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
        project: project,
        active: false,
        category: 'deployment',
        type: 'KubernetesService',
        properties: "{\"namespace\":\"prod\",\"api_url\":\"http://111.111.111.111\",\"ca_pem\":\"a\",\"token\":\"\"}")
    end

    it 'does not migrate the KubernetesService and disables the kubernetes_service' do
      expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)

      project.clusters.last.tap do |cluster|
        expect(cluster.environment_scope).to eq('*')
        expect(cluster.platform_kubernetes.namespace).to eq('prod')
        expect(cluster.platform_kubernetes.api_url).to eq('http://111.111.111.111')
        expect(cluster.platform_kubernetes.ca_cert).to eq('a')
        expect(cluster.platform_kubernetes.token).to be_empty
        expect(project.kubernetes_service).not_to be_active
      end
    end
  end

  context 'when KubernetesService does not exist' do
    let!(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }

    it 'does not migrate the KubernetesService' do
      expect { migrate! }.not_to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }
    end
  end
end