#!/usr/bin/env python
#
# Copyright 2016-2018, 2021 VMware, Inc.  All rights reserved. -- VMware Confidential
#

import logging
import os
import shutil
import unittest
from distutils.version import LooseVersion
from importlib import import_module

logging.basicConfig(level=logging.INFO)

# Absolute import so that one can run the unitests from the endpoint-b2b folder
# with PYTHONPATH=$(dirname `pwd`) python3 -m unittest
# import_module is used because of the dash in the module's name.
module = import_module('endpoint-b2b.properties_util')

PROPERTIES_FILE = "test.properties"
PROPERTIES_FILE_CONTENTS = """\
property = 42
another.property = 84
"""
PROPERTY_NAME = "property"
BACKUP_DIR = "upgrade_auto_backup"
PROPERTIES_BCK_FILE = os.path.join(BACKUP_DIR, PROPERTIES_FILE)
NON_EXISTING_PROPERTY = "non.existing"

class PropertiesUtilTestCase(unittest.TestCase):
   """
   Unit tests for the "properties_util" module
   """

   def setUp(self):
      # Create "test.properties" in the current folder
      with open(PROPERTIES_FILE, "w") as fp:
         fp.write(PROPERTIES_FILE_CONTENTS)

   def tearDown(self):
      if os.path.exists(PROPERTIES_FILE):
         os.remove(PROPERTIES_FILE)

      if os.path.exists(BACKUP_DIR):
         shutil.rmtree(BACKUP_DIR)

   def test_backup(self):
      """
      Test backing up the properties file
      """

      backup_file_path = module.backup(PROPERTIES_FILE)

      # Verify the result
      self.assertEqual(PROPERTIES_BCK_FILE, backup_file_path)
      self._assert_file_is_untouched()
      with open(PROPERTIES_BCK_FILE) as fp:
         content = fp.read()
         self.assertEqual(content, PROPERTIES_FILE_CONTENTS)

   def test_backup_does_nothing_if_exists(self):
      """
      Test backing up the proprieties file more than once
      """

      backup_file_path = module.backup(PROPERTIES_FILE)

      property_new_value = "46"
      property_new_key = "new.property"

      has_been_added = module.add(PROPERTIES_FILE,
                                  property_new_key,
                                  property_new_value)

      backup_file_path = module.backup(PROPERTIES_FILE)

      # Verify the result
      self.assertTrue(has_been_added)
      self.assertEqual(PROPERTIES_BCK_FILE, backup_file_path)
      self._assert_the_other_property_is_untouched()
      with open(PROPERTIES_BCK_FILE) as fp:
         content = fp.read()
         self.assertEqual(content, PROPERTIES_FILE_CONTENTS)

   def test_restore_backup(self):
      """
      Test restoring the backup
      """

      backup_file_path = module.backup(PROPERTIES_FILE)

      property_new_value = "46"
      property_new_key = "new.property"

      has_been_added = module.add(PROPERTIES_FILE,
                                  property_new_key,
                                  property_new_value)

      module.revert_backup_files(".")

      # Verify the result
      self.assertTrue(has_been_added)
      self.assertEqual(PROPERTIES_BCK_FILE, backup_file_path)
      self._assert_file_is_untouched()
      with open(PROPERTIES_BCK_FILE) as fp:
         content = fp.read()
         self.assertEqual(content, PROPERTIES_FILE_CONTENTS)

   def test_restore_backup_when_no_backup_dir(self):
      """
      Test restoring the backup when there is nothing backed up
      """

      self.assertFalse(os.path.exists(BACKUP_DIR))

      module.revert_backup_files(".")

      self.assertFalse(os.path.exists(BACKUP_DIR))

   def test_delete_backup_files(self):
      """
      Test deleting the backup files
      """

      backup_file_path = module.backup(PROPERTIES_FILE)

      # Verify the result
      self.assertEqual(PROPERTIES_BCK_FILE, backup_file_path)

      module.delete_backup_files(".")

      self.assertFalse(os.path.exists(BACKUP_DIR))

   def test_add(self):
      """
      Test adding a property
      """

      property_new_value = "46"
      property_new_key = "new.property"

      has_been_added = module.add(PROPERTIES_FILE,
                                  property_new_key,
                                  property_new_value)

      added_value = module.value(PROPERTIES_FILE, property_new_key)
      self.assertEqual(added_value, property_new_value)
      self.assertTrue(has_been_added)
      self._assert_the_other_property_is_untouched()

   def test_skip_adding_existing_property(self):
      """
      Test adding an already existing property
      """

      has_been_added = module.add(PROPERTIES_FILE,
                                  PROPERTY_NAME,
                                  "46")

      # Verify the result
      self._assert_file_is_untouched()
      self.assertFalse(has_been_added)

   def test_delete(self):
      """
      Test deleting a property
      """

      has_been_deleted = module.delete(PROPERTIES_FILE, PROPERTY_NAME)

      # Verify the result
      deleted_value = module.value(PROPERTIES_FILE, PROPERTY_NAME)
      self.assertIsNone(deleted_value)
      self.assertTrue(has_been_deleted)
      self._assert_the_other_property_is_untouched()

   def test_delete_non_existing(self):
      """
      Test deleting a property that's not there
      """

      has_been_deleted = module.delete(PROPERTIES_FILE,
                                       NON_EXISTING_PROPERTY)

      # Verify the result
      self._assert_file_is_untouched()
      self.assertFalse(has_been_deleted)

   def test_replace(self):
      """
      Test replacing a property's value
      """

      property_new_value = "46"
      has_been_replaced = module.replace(PROPERTIES_FILE,
                                         PROPERTY_NAME,
                                         property_new_value)

      # Verify the result
      self.assertTrue(has_been_replaced)

      #Verify the property is in its original place and the value has changed
      with open(PROPERTIES_FILE) as fr:
         first_line = fr.readline()
         self.assertEqual(first_line,
                          "%s = %s\n" % (PROPERTY_NAME, property_new_value))

      self._assert_the_other_property_is_untouched()

   def test_replace_non_existing(self):
      """
      Test replacing a property that's not there
      """

      has_been_replaced = module.replace(PROPERTIES_FILE,
                                         NON_EXISTING_PROPERTY,
                                         "42")

      # Verify the result
      self._assert_file_is_untouched()
      self.assertFalse(has_been_replaced)

   def test_get_value(self):
      """
      Test retrieving a property's value
      """

      value = module.value(PROPERTIES_FILE, PROPERTY_NAME)

      # Verify the result
      self.assertEqual("42", value)

   def test_get_non_existing_property_value(self):
      """
      Test retrieving a non-existing property value
      """

      value = module.value(PROPERTIES_FILE, NON_EXISTING_PROPERTY)

      # Verify the result
      self.assertIsNone(value)

   def _assert_file_is_untouched(self):
      with open(PROPERTIES_FILE) as fp:
         content = fp.read()
         self.assertEqual(content, PROPERTIES_FILE_CONTENTS)

   def _assert_the_other_property_is_untouched(self):
      another_property_value = module.value(PROPERTIES_FILE,
                                            "another.property")
      self.assertEqual("84", another_property_value)
