# -*- coding: utf-8 -*-
"""
Unit/regression tests for voidoi.

This MUST NOT run on a production site; it may nuke everything or introduce
invalid DOIs.
"""

import datetime
import os
os.environ["GAVO_OOTTEST"] = "dontcare"

from gavo import api
from gavo import utils
from gavo.helpers import testhelpers
from gavo.protocols import oaiclient

api.base.DEBUG = True;api.LoggingUI(api.base.ui)

RD = api.getRD("voidoi/q")
modName = os.path.join(RD.resdir, "res/registration")
registration, _ = utils.loadPythonModule(modName)
registration.RD = RD
registration.initModule(testing=True)
modName = os.path.join(RD.resdir, "res/landingpage")
landingpage, _ = utils.loadPythonModule(modName)
landingpage.RD = RD
landingpage.initModule(testing=True)


SAMPLE_RECORD = oaiclient._addCanonicalNSDecls("""
<oai:record>
	<oai:header>
		<oai:identifier>ivo://org.gavo.dc/std/glots</oai:identifier>
		<oai:datestamp>2016-01-21T15:52:38Z</oai:datestamp>
	</oai:header>
	<oai:metadata>

<ri:Resource status="active" xsi:type="vstd:Standard" updated="2015-07-21T07:07:39" created="2015-07-14T07:49:00Z"><title>The Global TAP Schema GloTS</title><identifier>ivo://org.gavo.dc/std/glots</identifier>
<altIdentifier>doi:10.5072/7a6KXm7hR9I6iVE46DEaRA</altIdentifier><curation><publisher>The GAVO DC team</publisher><creator><name>Markus Demleitner</name></creator><date role="updated">2015-07-21T07:07:39</date><contact><name>GAVO Data Center Team</name><address>Mönchhofstrasse 12-14, D-69120 Heidelberg</address><email>msdemlei@ari.uni-heidelberg.de</email><telephone>++49 6221 54 1837</telephone></contact></curation><content><subject>Virtual observatory</subject><subject>Standards</subject><subject>TAP_SCHEMA</subject><description> The Global TAP schema is an attempt at a simple way to present a
union of all TAP_SCHEMA instances globally, where right now GloTS only
contains tables and columns. It is primarily intended as a quick (and
relatively dirty) way for data collection discovery until other means
are ready.</description><referenceURL>http://ivoa.net/documents/discoveringcollections</referenceURL></content><endorsedVersion status="note" use="preferred">1.0</endorsedVersion><key><name>tables-1.0</name><description>The data model (as used in TAPRegExt's dataModel element), consisting
of glots.services, glots.tables, and glots.columns.</description></key></ri:Resource>
</oai:metadata></oai:record>
""")

SERVICE_RECORD = oaiclient._addCanonicalNSDecls("""
<oai:record>
	<oai:header>
		<oai:identifier>ivo://org.gavo.dc/std/glots</oai:identifier>
		<oai:datestamp>2016-01-21T15:52:38Z</oai:datestamp>
	</oai:header>
	<oai:metadata>

<ri:Resource status="active" xsi:type="vs:CatalogService" updated="2016-06-09T15:32:20" created="2009-07-21T08:32:00"><title>A Database of Circumstellar OH Masers</title><shortName>engels ohmasers</shortName><identifier>ivo://org.gavo.dc/ohmaser/q/scs</identifier>
<altIdentifier>doi:10.5072/7a6KXm7hR9I6iVE46DEaRA</altIdentifier>
<curation><publisher>The GAVO DC team</publisher><creator><name>Engels, D.</name><logo> http://dc.g-vo.org/ohmaser/q/scs/static/uhlogo.png</logo></creator><creator><name>Bunzel, F.</name></creator><date role="updated">2016-06-09T15:32:20</date><date role="updated">2015-07-09</date><version>2.4</version><contact><name>GAVO Data Center Team</name><address>Mönchhofstrasse 12-14, D-69120 Heidelberg</address><email>gavo@ari.uni-heidelberg.de</email><telephone>++49 6221 54 1837</telephone></contact></curation><content><subject>Masers</subject><subject>Catalogs</subject><subject>Stars: AGB and post-AGB</subject><subject>Stars: late-type</subject><description>A all-sky compilation of galactic stellar sources observed for OH
maser emission in the transitions at 1612, 1665, and 1667 MHz. The
database contains OH maser observations selected from the literature .
These observations belong to more than 6000 different objects. The
database consists of three tables: The main table ("masers"),
interferometric followup observations ("maps") and monitoring programs
("monitor").</description><source format="bibcode">2015A&amp;A...582A..68E</source><referenceURL>http://dc.zah.uni-heidelberg.de/ohmaser/q/scs/info</referenceURL><relationship><relationshipType>served-by</relationshipType><relatedResource ivo-id="ivo://org.gavo.dc/tap">GAVO Data Center TAP service</relatedResource></relationship></content>
<capability><interface xsi:type="vr:WebBrowser"><accessURL use="full">http://www.hs.uni-hamburg.de/~st2b102/maserdb/</accessURL></interface></capability>
<capability xsi:type="cs:ConeSearch" standardID="ivo://ivoa.net/std/ConeSearch"><interface xsi:type="vs:ParamHTTP" role="std"><accessURL use="base">http://dc.zah.uni-heidelberg.de/ohmaser/q/scs/scs.xml?</accessURL><queryType>GET</queryType><resultType>application/x-votable+xml</resultType><param std="true"><name>RA</name><description>Right Ascension (ICRS decimal)</description><unit>deg</unit><ucd>pos.eq.ra</ucd><dataType>real</dataType></param><param std="true"><name>DEC</name><description>Declination (ICRS decimal)</description><unit>deg</unit><ucd>pos.eq.dec</ucd><dataType>real</dataType></param></interface><maxSR>180</maxSR><maxRecords>100000</maxRecords><verbosity>true</verbosity><testQuery><ra>4.190417</ra><dec>10.244445</dec><sr>0.001</sr></testQuery></capability>
<capability standardID="ivo://ivoa.net/std/VOSI#availability"><interface xsi:type="vs:ParamHTTP"><accessURL use="full">http://dc.zah.uni-heidelberg.de/ohmaser/q/scs/availability</accessURL></interface></capability><capability standardID="ivo://ivoa.net/std/VOSI#capabilities"><interface xsi:type="vs:ParamHTTP"><accessURL use="full">http://dc.zah.uni-heidelberg.de/ohmaser/q/scs/capabilities</accessURL></interface></capability><capability standardID="ivo://ivoa.net/std/VOSI#tables"><interface xsi:type="vs:ParamHTTP"><accessURL use="full">http://dc.zah.uni-heidelberg.de/ohmaser/q/scs/tableMetadata</accessURL></interface></capability><capability standardID="ivo://ivoa.net/std/TAP#aux"><interface xsi:type="vs:ParamHTTP" role="std"><accessURL use="base">http://dc.zah.uni-heidelberg.de/__system__/tap/run/tap</accessURL></interface></capability>
<coverage><waveband>Radio</waveband></coverage>
</ri:Resource>
</oai:metadata></oai:record>""")

def _makeTestRecord(ivoid="ivo://org.gavo.dc/std/glots",
		date_reg=datetime.datetime(2015, 10, 20, 11, 21, 00),
		doi=None, full_record=SAMPLE_RECORD,
		last_confirmation_code="testing,ignore"):
	return locals()


class MiscTest(testhelpers.VerboseTest):
	pass


class DOIsTable(testhelpers.TestResource):
	def make(self, deps):
		self.conn = api.getDBConnection("admin")
		self.table = api.TableForDef(RD.getById("dois"), create=True,
			connection=self.conn)
		return self.table
	
	def cleanup(self, deps):
		self.table.close()
		self.conn.close()

doisTable = DOIsTable()


class GlotsRecord(testhelpers.TestResource):
	resources = [("doisTable", doisTable)]

	def make(self, deps):
		self.doisTable = deps["doisTable"]
		self.res = registration.storeRequest(self.doisTable,
			"ivo://org.gavo.dc/std/glots",
			SAMPLE_RECORD)
		self.doisTable.connection.commit()
		return self.res

	def isDirty(self):
		return "dirty" in self.res

	def clean(self, res):
		self.doisTable.deleteMatching("ivoid=%(ivoid)s",
			{"ivoid": "ivo://org.gavo.dc/std/glots"})
		self.doisTable.connection.commit()

glotsRecord = GlotsRecord()


class RequestCreationTest(testhelpers.VerboseTest):
	resources = [("rec", glotsRecord), ("doisTable", doisTable)]

	def testIvoid(self):
		self.assertEqual(self.rec["ivoid"], "ivo://org.gavo.dc/std/glots")
	
	def testConfirmationCodeDefined(self):
		self.assertTrue(self.rec["last_confirmation_code"] is not None)
		self.assertNotEqual(self.rec["last_confirmation_code"],
			registration.makeConfirmationKey())

	def testNoDOIYet(self):
		self.assertEqual(self.rec["doi"], None)
	
	def testPlausibleCreationDate(self):
		self.assertTrue(
			((datetime.datetime.utcnow()-self.rec["date_reg"]).seconds<=2))
	
	def testFullRecordPresent(self):
		self.assertTrue("2015-07-14T07:49:00Z" in self.rec["full_record"])

	def testConfirmationURL(self):
		url = registration._makeConfirmationURL(self.rec)
		self.assertTrue(url.startswith(api.getConfig("web", "serverURL")))
		self.assertTrue(url.endswith(self.rec["last_confirmation_code"]))
		self.assertTrue("/ui/" in url)

	def testConfirmationMail(self):
		res, contactAddress = registration.prepareMailing(self.rec)
		self.assertEqual(contactAddress, "msdemlei@ari.uni-heidelberg.de")
		self.assertTrue("custom/confirm/"+self.rec["last_confirmation_code"]
			in res)

	def testInDB(self):
		with api.getTableConn() as conn:
			self.assertEqual(
				self.rec.original,
				list(conn.queryToDicts("select * from voidoi.dois"
					" where ivoid like '%%std/glots'"))[0])

	def testWellformedSucceeds(self):
		try:
			warnings, resTree = registration.checkTransformation(self.rec)
		except api.ValidationError as msg:
			print(">>>>>>>>>>", msg.hint)
			raise
		self.assertEqual(warnings, [])
		ns = {"d": "http://datacite.org/schema/kernel-4"}
		self.assertTrue("is an attempt" in
			resTree.xpath("//d:description[@descriptionType='Abstract']",
				namespaces=ns)[0].text)
		self.assertEqual("Markus Demleitner",
			resTree.xpath("//d:creatorName", namespaces=ns)[0].text)

	def testNonwellformedFails(self):
		row = _makeTestRecord(full_record="<invalid></broken>")
		self.assertRaisesWithMsg(api.ValidationError,
			"Field ivoid: The resource record is invalid -- you need to fix it"
			" first (see below for a hint on where the problem is).",
			registration.checkTransformation,
			(row,))

	def testInvalidFails(self):
		row = _makeTestRecord()
		row["full_record"] = row["full_record"
			].replace("2015-07-21", "2015-17-21")
		self.assertRaisesWithMsg(api.ValidationError,
			"Field ivoid: The resource record is invalid -- you need to fix it"
			" first (see below for a hint on where the problem is).",
			registration.checkTransformation,
			(row,))


class ConfirmedRecord(testhelpers.TestResource):
	resources = [("glotsRecord", glotsRecord), ("doisTable", doisTable)]

	def make(self, deps):
		res = registration.runConfirmation(
			deps["glotsRecord"]["last_confirmation_code"])
		deps["doisTable"].connection.commit()
		deps["glotsRecord"].original["dirty"] = True
		return res
	

confirmedRecord = ConfirmedRecord()

class ConfirmedTest(testhelpers.VerboseTest):
	resources = [("rec", confirmedRecord)]

	def testDOICreated(self):
		self.assertEqual(self.rec["doi"], "10.5072/7a6KXm7hR9I6iVE46DEaRA")


class LandingPage(testhelpers.TestResource):
	resources = [("rec", glotsRecord), ("doisTable", doisTable)]

	def make(self, deps):
		deps["doisTable"].connection.execute("update voidoi.dois"
			" set doi='10.5072/7a6KXm7hR9I6iVE46DEaRA'"
			" where ivoid like '%%std/glots'")
		deps["doisTable"].connection.commit()
		try:
			doc = landingpage.LandingPage("10.5072/7a6KXm7hR9I6iVE46DEaRA"
				).render(None)
			return testhelpers.getXMLTree(doc)
		finally:
			deps["doisTable"].connection.execute("update voidoi.dois"
				" set doi=NULL"
				" where doi='10.5072/7a6KXm7hR9I6iVE46DEaRA'")
			deps["doisTable"].connection.commit()


class ServiceLandingPage(testhelpers.TestResource):
	resources = [("doisTable", doisTable)]

	def make(self, deps):
		deps["doisTable"].connection.execute("insert into voidoi.dois"
			" (ivoid, date_reg, doi, full_record)"
			" values ('ivo://org.gavo.dc/ohmaser/q/scs',"
			"  '2016-01-01', '10.5072/servicerecord', %(rec)s)",
			{"rec": SERVICE_RECORD})
		deps["doisTable"].connection.commit()
		try:
			doc = landingpage.LandingPage("10.5072/servicerecord"
				).render(None)
			return testhelpers.getXMLTree(doc, debug=False)
		finally:
			deps["doisTable"].connection.execute("delete from voidoi.dois"
				" where doi='10.5072/servicerecord'")
			deps["doisTable"].connection.commit()


class LandingPageTest(testhelpers.VerboseTest):
	resources = [("tree", LandingPage()), ("servicetree", ServiceLandingPage())]

	def testTitle(self):
		self.assertEqual(self.tree.xpath("/html/head/title")[0].text,
			"The Global TAP Schema GloTS")

	def testHeadline1(self):
		self.assertEqual(self.tree.xpath("/html/body/h1/span")[0].text,
			"The Global TAP Schema GloTS")

	def testDOI(self):
		dest = self.servicetree.xpath(
			"//dt[span[@class='protocol']/.='DOI']/following-sibling::dd/span/a")[0]
		self.assertEqual(dest.text, "doi:10.5072/7a6KXm7hR9I6iVE46DEaRA")
		self.assertEqual(dest.get("href"), "https://doi.org/10.5072/7a6KXm7hR9I6iVE46DEaRA")

	def testHeadline2(self):
		self.assertEqual(self.servicetree.xpath("/html/body/h1/span")[0].text,
			"A Database of Circumstellar OH Masers")
	
	def testBibcodeURL(self):
		dest = self.servicetree.xpath(
			"//dt[span[@class='protocol']='Bibcode']/following-sibling::dd")[0]
		self.assertEqual(dest.xpath("a")[0].get("href"),
			"https://adsabs.harvard.edu/abs/2015A&A...582A..68E")

	def testSubjects(self):
		subjs = self.servicetree.xpath(
			"//dt[.='Keywords']/following-sibling::dd")[0]
		self.assertEqual(set(e.text for e in subjs.xpath("ol/li")),
			set(["Masers", "Catalogs", "Stars: AGB and post-AGB",
				"Stars: late-type"]))
	
	def testDatesSorted(self):
		dest = self.servicetree.xpath(
			"//div[@id='dates']/dl")[0]
		datesSeq = [d.text for d in dest.xpath("dt/span")]
		self.assertEqual(
			datesSeq,
			['2009-07-21T08:32:00', '2015-07-09', '2016-06-09T15:32:20'])
	
	def testWebCapability(self):
		dest = self.servicetree.xpath(
			"//h2[.='Access']/following-sibling::dl/dt[@class='browsable']"
				"/following-sibling::dd")[0]
		url = "http://www.hs.uni-hamburg.de/~st2b102/maserdb/"
		self.assertEqual(dest.xpath("a")[0].get("href"),
			"http://www.hs.uni-hamburg.de/~st2b102/maserdb/")
		self.assertEqual(dest.xpath("a")[0].text,
			"http://www.hs.uni-hamburg.de/~st2b102/maserdb/")

	def testCSCapability(self):
		dest = self.servicetree.xpath(
			"//dt[span[@class='protocol']='SCS']/following-sibling::dd")[0]
		self.assertTrue("cone search client" in dest.text)
		self.assertEqual(dest.xpath("span/a")[0].text,
			"http://dc.zah.uni-heidelberg.de/ohmaser/q/scs/scs.xml?")
		self.assertEqual(dest.xpath("span/a")[0].get("href"),
			"http://dc.zah.uni-heidelberg.de/ohmaser/q/scs/scs.xml?")


if __name__=="__main__":
	testhelpers.main(MiscTest)
