from slapos.grid.promise import PromiseError
from slapos.test.promise.plugin import TestPromisePluginMixin

import os
import shutil
import tempfile


class CheckSurykatkaJSONMixin(TestPromisePluginMixin):
  promise_name = 'check-surykatka-json.py'

  def setUp(self):
    self.working_directory = tempfile.mkdtemp()
    self.json_file = os.path.join(self.working_directory, 'surykatka.json')
    self.addCleanup(shutil.rmtree, self.working_directory)
    TestPromisePluginMixin.setUp(self)

  def writeSurykatkaPromise(self, d=None):
    if d is None:
      d = {}
    content_list = [
      "from slapos.promise.plugin.check_surykatka_json import RunPromise"]
    content_list.append('extra_config_dict = {')
    for k, v in d.items():
      content_list.append("  '%s': '%s'," % (k, v))
    content_list.append('}')
    self.writePromise(self.promise_name, '\n'.join(content_list))

  def writeSurykatkaJson(self, content):
    with open(self.json_file, 'w') as fh:
      fh.write(content)

  def assertFailedMessage(self, result, message):
    self.assertEqual(result['result']['failed'], True)
    self.assertEqual(
      result['result']['message'],
      message)

  def assertPassedMessage(self, result, message):
    self.assertEqual(result['result']['failed'], False)
    self.assertEqual(
      result['result']['message'],
      message)


class TestCheckSurykatkaJSON(CheckSurykatkaJSONMixin):
  def test_no_config(self):
    self.writeSurykatkaPromise()
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "File '' does not exists")

  def test_not_existing_file(self):
    self.writeSurykatkaPromise({'json-file': self.json_file})
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "File '%s' does not exists" % (self.json_file,))

  def test_empty_file(self):
    self.writeSurykatkaPromise({'json-file': self.json_file})
    self.writeSurykatkaJson('')
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "Problem loading JSON from '%s'" % (self.json_file,))


class TestCheckSurykatkaJSONUnknownReport(CheckSurykatkaJSONMixin):
  def test(self):
    self.writeSurykatkaPromise(
      {
        'report': 'NOT_EXISTING_ENTRY',
        'json-file': self.json_file,
      }
    )
    self.writeSurykatkaJson("""{
}
""")
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "Report 'NOT_EXISTING_ENTRY' is not supported")


class TestCheckSurykatkaJSONBotStatus(CheckSurykatkaJSONMixin):
  def test(self):
    self.writeSurykatkaPromise(
      {
        'report': 'bot_status',
        'json-file': self.json_file,
        'test-utcnow': 'Wed, 13 Dec 2222 09:11:12 -0000'
      }
    )
    self.writeSurykatkaJson("""{
    "bot_status": [
        {
            "date": "Wed, 13 Dec 2222 09:10:11 -0000",
            "text": "loop"
        }
    ]
}
""")
    self.configureLauncher()
    self.launcher.run()
    self.assertPassedMessage(
      self.getPromiseResult(self.promise_name),
      "bot_status: Last bot status from 2222-12-13 09:10:11 ok, "
      "UTC now is 2222-12-13 09:11:12"
    )

  def test_bot_status_future(self):
    self.writeSurykatkaPromise(
      {
        'report': 'bot_status',
        'json-file': self.json_file,
        'test-utcnow': 'Wed, 13 Dec 2222 09:11:12 -0000'
      }
    )
    self.writeSurykatkaJson("""{
    "bot_status": [
        {
            "date": "Wed, 13 Dec 2223 09:10:11 -0000",
            "text": "loop"
        }
    ]
}
""")
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "bot_status: Last bot datetime 2223-12-13 09:10:11 is in "
      "future, UTC now 2222-12-13 09:11:12"
    )

  def test_bot_status_old(self):
    self.writeSurykatkaPromise(
      {
        'report': 'bot_status',
        'json-file': self.json_file,
        'test-utcnow': 'Wed, 13 Dec 2223 09:26:12 -0000'
      }
    )
    self.writeSurykatkaJson("""{
    "bot_status": [
        {
            "date": "Wed, 13 Dec 2223 09:10:11 -0000",
            "text": "loop"
        }
    ]
}
""")
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "bot_status: Last bot datetime 2223-12-13 09:10:11 is "
      "more than 15 minutes old, UTC now 2223-12-13 09:26:12"
    )

  def test_not_bot_status(self):
    self.writeSurykatkaPromise(
      {
        'report': 'bot_status',
        'json-file': self.json_file,
      }
    )
    self.writeSurykatkaJson("""{
}
""")
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "bot_status: 'bot_status' not in '%s'" % (self.json_file,))

  def test_empty_bot_status(self):
    self.writeSurykatkaPromise(
      {
        'report': 'bot_status',
        'json-file': self.json_file,
      }
    )
    self.writeSurykatkaJson("""{
  "bot_status": []
}
""")
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "bot_status: 'bot_status' empty in '%s'" % (self.json_file,))


class TestCheckSurykatkaJSONHttpQuery(CheckSurykatkaJSONMixin):
  def test(self):
    self.writeSurykatkaPromise(
      {
        'report': 'http_query',
        'json-file': self.json_file,
        'url': 'https://www.erp5.com/',
        'status-code': '302',
        'ip-list': '127.0.0.1 127.0.0.2',
        'test-utcnow': 'Fri, 27 Dec 2019 15:11:12 -0000'
      }
    )
    self.writeSurykatkaJson("""{
    "http_query": [
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "127.0.0.1",
            "status_code": 302,
            "url": "https://www.erp5.com/"
        },
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "127.0.0.2",
            "status_code": 302,
            "url": "https://www.erp5.com/"
        },
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "176.31.129.213",
            "status_code": 200,
            "url": "https://www.erp5.org/"
        }
    ],
    "ssl_certificate": [
        {
            "date": "Fri, 27 Dec 2019 14:43:26 -0000",
            "hostname": "www.erp5.com",
            "ip": "127.0.0.1",
            "not_after": "Mon, 13 Jul 2020 12:00:00 -0000"
        },
        {
            "date": "Fri, 27 Dec 2019 14:43:26 -0000",
            "hostname": "www.erp5.com",
            "ip": "127.0.0.2",
            "not_after": "Mon, 13 Jul 2020 12:00:00 -0000"
        }
    ]
}
""")
    self.configureLauncher()
    self.launcher.run()
    self.assertPassedMessage(
      self.getPromiseResult(self.promise_name),
      "http_query: https://www.erp5.com/ replied correctly with "
      "status code 302 on ip list 127.0.0.1 127.0.0.2 ssl_certificate: "
      "Certificate for https://www.erp5.com/ will expire on Mon, 13 Jul "
      "2020 12:00:00 -0000, which is more than 15 days, UTC now is "
      "Fri, 27 Dec 2019 15:11:12 -0000"
    )

  def test_no_ip_list(self):
    self.writeSurykatkaPromise(
      {
        'report': 'http_query',
        'json-file': self.json_file,
        'url': 'https://www.erp5.com/',
        'status-code': '302',
        'test-utcnow': 'Fri, 27 Dec 2019 15:11:12 -0000'
      }
    )
    self.writeSurykatkaJson("""{
    "http_query": [
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "127.0.0.1",
            "status_code": 302,
            "url": "https://www.erp5.com/"
        },
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "127.0.0.2",
            "status_code": 302,
            "url": "https://www.erp5.com/"
        },
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "176.31.129.213",
            "status_code": 200,
            "url": "https://www.erp5.org/"
        }
    ],
    "ssl_certificate": [
        {
            "date": "Fri, 27 Dec 2019 14:43:26 -0000",
            "hostname": "www.erp5.com",
            "ip": "127.0.0.1",
            "not_after": "Mon, 13 Jul 2020 12:00:00 -0000"
        },
        {
            "date": "Fri, 27 Dec 2019 14:43:26 -0000",
            "hostname": "www.erp5.com",
            "ip": "127.0.0.2",
            "not_after": "Mon, 13 Jul 2020 12:00:00 -0000"
        }
    ]
}
""")
    self.configureLauncher()
    self.launcher.run()
    self.assertPassedMessage(
      self.getPromiseResult(self.promise_name),
      "http_query: https://www.erp5.com/ replied correctly with status "
      "code 302 ssl_certificate: Certificate for https://www.erp5.com/ will "
      "expire on Mon, 13 Jul 2020 12:00:00 -0000, which is more than 15 "
      "days, UTC now is Fri, 27 Dec 2019 15:11:12 -0000"
    )

  def test_bad_code(self):
    self.writeSurykatkaPromise(
      {
        'report': 'http_query',
        'json-file': self.json_file,
        'url': 'https://www.erp5.com/',
        'status-code': '301',
        'test-utcnow': 'Fri, 27 Dec 2019 15:11:12 -0000'
      }
    )
    self.writeSurykatkaJson("""{
    "http_query": [
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "127.0.0.1",
            "status_code": 302,
            "url": "https://www.erp5.com/"
        },
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "127.0.0.2",
            "status_code": 301,
            "url": "https://www.erp5.com/"
        },
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "176.31.129.213",
            "status_code": 200,
            "url": "https://www.erp5.org/"
        }
    ],
    "ssl_certificate": [
        {
            "date": "Fri, 27 Dec 2019 14:43:26 -0000",
            "hostname": "www.erp5.com",
            "ip": "127.0.0.1",
            "not_after": "Mon, 13 Jul 2020 12:00:00 -0000"
        },
        {
            "date": "Fri, 27 Dec 2019 14:43:26 -0000",
            "hostname": "www.erp5.com",
            "ip": "127.0.0.2",
            "not_after": "Mon, 13 Jul 2020 12:00:00 -0000"
        }
    ]
}
""")
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "http_query: Problem with https://www.erp5.com/: IP 127.0.0.1 got "
      "status code 302 instead of 301 ssl_certificate: Certificate for "
      "https://www.erp5.com/ will expire on Mon, 13 Jul 2020 12:00:00 "
      "-0000, which is more than 15 days, UTC now is Fri, 27 Dec 2019 "
      "15:11:12 -0000"
    )

  def test_bad_ip(self):
    self.writeSurykatkaPromise(
      {
        'report': 'http_query',
        'json-file': self.json_file,
        'url': 'https://www.erp5.com/',
        'status-code': '301',
        'ip-list': '127.0.0.1 127.0.0.2',
        'test-utcnow': 'Fri, 27 Dec 2019 15:11:12 -0000'
      }
    )
    self.writeSurykatkaJson("""{
    "http_query": [
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "127.0.0.1",
            "status_code": 301,
            "url": "https://www.erp5.com/"
        },
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "127.0.0.4",
            "status_code": 301,
            "url": "https://www.erp5.com/"
        },
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "176.31.129.213",
            "status_code": 200,
            "url": "https://www.erp5.org/"
        }
    ],
    "ssl_certificate": [
        {
            "date": "Fri, 27 Dec 2019 14:43:26 -0000",
            "hostname": "www.erp5.com",
            "ip": "127.0.0.1",
            "not_after": "Mon, 13 Jul 2020 12:00:00 -0000"
        },
        {
            "date": "Fri, 27 Dec 2019 14:43:26 -0000",
            "hostname": "www.erp5.com",
            "ip": "127.0.0.2",
            "not_after": "Mon, 13 Jul 2020 12:00:00 -0000"
        }
    ]
}
""")
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "http_query: Problem with https://www.erp5.com/: expected IPs "
      "127.0.0.1 127.0.0.2 differes from got 127.0.0.1 127.0.0.4 "
      "ssl_certificate: Certificate for https://www.erp5.com/ will expire "
      "on Mon, 13 Jul 2020 12:00:00 -0000, which is more than 15 days, "
      "UTC now is Fri, 27 Dec 2019 15:11:12 -0000"
    )

  def test_bad_ip_status_code(self):
    self.writeSurykatkaPromise(
      {
        'report': 'http_query',
        'json-file': self.json_file,
        'url': 'https://www.erp5.com/',
        'status-code': '301',
        'ip-list': '127.0.0.1 127.0.0.2',
        'test-utcnow': 'Fri, 27 Dec 2019 15:11:12 -0000'
      }
    )
    self.writeSurykatkaJson("""{
    "http_query": [
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "127.0.0.1",
            "status_code": 302,
            "url": "https://www.erp5.com/"
        },
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "127.0.0.4",
            "status_code": 301,
            "url": "https://www.erp5.com/"
        },
        {
            "date": "Wed, 11 Dec 2019 09:35:28 -0000",
            "ip": "176.31.129.213",
            "status_code": 200,
            "url": "https://www.erp5.org/"
        }
    ],
    "ssl_certificate": [
        {
            "date": "Fri, 27 Dec 2019 14:43:26 -0000",
            "hostname": "www.erp5.com",
            "ip": "127.0.0.1",
            "not_after": "Mon, 13 Jul 2020 12:00:00 -0000"
        },
        {
            "date": "Fri, 27 Dec 2019 14:43:26 -0000",
            "hostname": "www.erp5.com",
            "ip": "127.0.0.2",
            "not_after": "Mon, 13 Jul 2020 12:00:00 -0000"
        }
    ]
}
""")
    self.configureLauncher()
    with self.assertRaises(PromiseError):
      self.launcher.run()
    self.assertFailedMessage(
      self.getPromiseResult(self.promise_name),
      "http_query: Problem with https://www.erp5.com/: IP 127.0.0.1 got "
      "status code 302 instead of 301, expected IPs 127.0.0.1 127.0.0.2 "
      "differes from got 127.0.0.1 127.0.0.4 ssl_certificate: Certificate "
      "for https://www.erp5.com/ will expire on Mon, 13 Jul 2020 12:00:00 "
      "-0000, which is more than 15 days, UTC now is Fri, 27 Dec 2019 "
      "15:11:12 -0000"
    )