import unittest from datetime import datetime, timedelta import tempfile import logging import pgmon # Silence most logging output logging.disable(logging.CRITICAL) class TestPgmonMethods(unittest.TestCase): ## # update_deep ## def test_update_deep__empty_cases(self): # Test empty dict cases d1 = {} d2 = {} pgmon.update_deep(d1, d2) self.assertEqual(d1, {}) self.assertEqual(d2, {}) d1 = {"a": 1} d2 = {} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"a": 1}) self.assertEqual(d2, {}) d1 = {} d2 = {"a": 1} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"a": 1}) self.assertEqual(d2, d1) def test_update_deep__scalars(self): # Test adding/updating scalar values d1 = {"foo": 1, "bar": "text", "hello": "world"} d2 = {"foo": 2, "baz": "blah"} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"foo": 2, "bar": "text", "baz": "blah", "hello": "world"}) self.assertEqual(d2, {"foo": 2, "baz": "blah"}) def test_update_deep__lists(self): # Test adding to lists d1 = {"lst1": []} d2 = {"lst1": [1, 2]} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"lst1": [1, 2]}) self.assertEqual(d2, d1) d1 = {"lst1": [1, 2]} d2 = {"lst1": []} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"lst1": [1, 2]}) self.assertEqual(d2, {"lst1": []}) d1 = {"lst1": [1, 2, 3]} d2 = {"lst1": [3, 4]} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"lst1": [1, 2, 3, 3, 4]}) self.assertEqual(d2, {"lst1": [3, 4]}) # Lists of objects d1 = {"lst1": [{"id": 1}, {"id": 2}, {"id": 3}]} d2 = {"lst1": [{"id": 3}, {"id": 4}]} pgmon.update_deep(d1, d2) self.assertEqual( d1, {"lst1": [{"id": 1}, {"id": 2}, {"id": 3}, {"id": 3}, {"id": 4}]} ) self.assertEqual(d2, {"lst1": [{"id": 3}, {"id": 4}]}) # Nested lists d1 = {"obj1": {"l1": [1, 2]}} d2 = {"obj1": {"l1": [3, 4]}} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"obj1": {"l1": [1, 2, 3, 4]}}) self.assertEqual(d2, {"obj1": {"l1": [3, 4]}}) def test_update_deep__dicts(self): # Test adding to lists d1 = {"obj1": {}} d2 = {"obj1": {"a": 1, "b": 2}} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"obj1": {"a": 1, "b": 2}}) self.assertEqual(d2, d1) d1 = {"obj1": {"a": 1, "b": 2}} d2 = {"obj1": {}} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"obj1": {"a": 1, "b": 2}}) self.assertEqual(d2, {"obj1": {}}) d1 = {"obj1": {"a": 1, "b": 2}} d2 = {"obj1": {"a": 5, "c": 12}} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"obj1": {"a": 5, "b": 2, "c": 12}}) self.assertEqual(d2, {"obj1": {"a": 5, "c": 12}}) # Nested dicts d1 = {"obj1": {"d1": {"a": 1, "b": 2}}} d2 = {"obj1": {"d1": {"a": 5, "c": 12}}} pgmon.update_deep(d1, d2) self.assertEqual(d1, {"obj1": {"d1": {"a": 5, "b": 2, "c": 12}}}) self.assertEqual(d2, {"obj1": {"d1": {"a": 5, "c": 12}}}) def test_update_deep__types(self): # Test mismatched types d1 = {"foo": 5} d2 = None self.assertRaises(TypeError, pgmon.update_deep, d1, d2) d1 = None d2 = {"foo": 5} self.assertRaises(TypeError, pgmon.update_deep, d1, d2) # Nested mismatched types d1 = {"foo": [1, 2]} d2 = {"foo": {"a": 7}} self.assertRaises(TypeError, pgmon.update_deep, d1, d2) ## # get_pool ## def test_get_pool__simple(self): # Just get a pool in a normal case pgmon.config.update(pgmon.default_config) pool = pgmon.get_pool("postgres") self.assertIsNotNone(pool) def test_get_pool__unhappy(self): # Test getting an unhappy database pool pgmon.config.update(pgmon.default_config) pgmon.unhappy_cooldown["postgres"] = datetime.now() + timedelta(60) self.assertRaises(pgmon.UnhappyDBError, pgmon.get_pool, "postgres") # Test getting a different database when there's an unhappy one pool = pgmon.get_pool("template0") self.assertIsNotNone(pool) ## # handle_connect_failure ## def test_handle_connect_failure__simple(self): # Test adding to an empty unhappy list pgmon.config.update(pgmon.default_config) pgmon.unhappy_cooldown = {} pool = pgmon.get_pool("postgres") pgmon.handle_connect_failure(pool) self.assertGreater(pgmon.unhappy_cooldown["postgres"], datetime.now()) # Test adding another database pool = pgmon.get_pool("template0") pgmon.handle_connect_failure(pool) self.assertGreater(pgmon.unhappy_cooldown["postgres"], datetime.now()) self.assertGreater(pgmon.unhappy_cooldown["template0"], datetime.now()) self.assertEqual(len(pgmon.unhappy_cooldown), 2) ## # get_query ## def test_get_query__basic(self): # Test getting a query with one version metric = {"type": "value", "query": {0: "DEFAULT"}} self.assertEqual(pgmon.get_query(metric, 100000), "DEFAULT") def test_get_query__versions(self): metric = {"type": "value", "query": {0: "DEFAULT", 110000: "NEW"}} # Test getting the default version of a query with no lower bound and a newer version self.assertEqual(pgmon.get_query(metric, 100000), "DEFAULT") # Test getting the newer version of a query with no lower bound and a newer version for the newer version self.assertEqual(pgmon.get_query(metric, 110000), "NEW") # Test getting the newer version of a query with no lower bound and a newer version for an even newer version self.assertEqual(pgmon.get_query(metric, 160000), "NEW") # Test getting a version in bwtween two other versions metric = {"type": "value", "query": {0: "DEFAULT", 96000: "OLD", 110000: "NEW"}} self.assertEqual(pgmon.get_query(metric, 100000), "OLD") def test_get_query__missing_version(self): metric = {"type": "value", "query": {96000: "OLD", 110000: "NEW", 150000: ""}} # Test getting a metric that only exists for newer versions self.assertRaises(pgmon.MetricVersionError, pgmon.get_query, metric, 80000) # Test getting a metric that only exists for older versions self.assertRaises(pgmon.MetricVersionError, pgmon.get_query, metric, 160000) ## # read_config ## def test_read_config__simple(self): pgmon.config = {} # Test reading just a metric and using the defaults for everything else with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- # This is a comment! metrics: test1: type: value query: 0: TEST1 """ ) pgmon.read_config(f"{tmpdirname}/config.yml") self.assertEqual( pgmon.config["max_pool_size"], pgmon.default_config["max_pool_size"] ) self.assertEqual(pgmon.config["dbuser"], pgmon.default_config["dbuser"]) pgmon.config = {} # Test reading a basic config with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- # This is a comment! min_pool_size: 1 max_pool_size: 2 max_idle_time: 10 log_level: debug dbuser: someone dbhost: localhost dbport: 5555 dbname: template0 pool_slot_timeout: 1 connect_timeout: 1 reconnect_cooldown: 15 version_check_period: 3600 metrics: test1: type: value query: 0: TEST1 test2: type: set query: 0: TEST2 test3: type: row query: 0: TEST3 test4: type: column query: 0: TEST4 """ ) pgmon.read_config(f"{tmpdirname}/config.yml") self.assertEqual(pgmon.config["dbuser"], "someone") self.assertEqual(pgmon.config["metrics"]["test1"]["type"], "value") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") self.assertEqual(pgmon.config["metrics"]["test2"]["query"][0], "TEST2") def test_read_config__include(self): pgmon.config = {} # Test reading a config that includes other files (absolute and relative paths, multiple levels) with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( f"""--- # This is a comment! min_pool_size: 1 max_pool_size: 2 max_idle_time: 10 log_level: debug pool_slot_timeout: 1 connect_timeout: 1 reconnect_cooldown: 15 version_check_period: 3600 include: - dbsettings.yml - {tmpdirname}/metrics.yml """ ) with open(f"{tmpdirname}/dbsettings.yml", "w") as f: f.write( f"""--- dbuser: someone dbhost: localhost dbport: 5555 dbname: template0 """ ) with open(f"{tmpdirname}/metrics.yml", "w") as f: f.write( f"""--- metrics: test1: type: value query: 0: TEST1 test2: type: value query: 0: TEST2 include: - more_metrics.yml """ ) with open(f"{tmpdirname}/more_metrics.yml", "w") as f: f.write( f"""--- metrics: test3: type: value query: 0: TEST3 """ ) pgmon.read_config(f"{tmpdirname}/config.yml") self.assertEqual(pgmon.config["max_idle_time"], 10) self.assertEqual(pgmon.config["dbuser"], "someone") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") self.assertEqual(pgmon.config["metrics"]["test2"]["query"][0], "TEST2") self.assertEqual(pgmon.config["metrics"]["test3"]["query"][0], "TEST3") def test_read_config__reload(self): pgmon.config = {} # Test rereading a config to update an existing config with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- # This is a comment! min_pool_size: 1 max_pool_size: 2 max_idle_time: 10 log_level: debug dbuser: someone dbhost: localhost dbport: 5555 dbname: template0 pool_slot_timeout: 1 connect_timeout: 1 reconnect_cooldown: 15 version_check_period: 3600 metrics: test1: type: value query: 0: TEST1 test2: type: value query: 0: TEST2 """ ) pgmon.read_config(f"{tmpdirname}/config.yml") # Just make sure the first config was read self.assertEqual(len(pgmon.config["metrics"]), 2) with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- # This is a comment! min_pool_size: 7 metrics: test1: type: value query: 0: NEW1 """ ) pgmon.read_config(f"{tmpdirname}/config.yml") self.assertEqual(pgmon.config["min_pool_size"], 7) self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "NEW1") self.assertEqual(len(pgmon.config["metrics"]), 1) def test_read_config__query_file(self): pgmon.config = {} # Read a config file that reads a query from a file with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- metrics: test1: type: value query: 0: file:some_query.sql """ ) with open(f"{tmpdirname}/some_query.sql", "w") as f: f.write("This is a query") pgmon.read_config(f"{tmpdirname}/config.yml") self.assertEqual( pgmon.config["metrics"]["test1"]["query"][0], "This is a query" ) def test_read_config__invalid(self): pgmon.config = {} # For all of these tests, we start with a valid config and also ensure that # it is not modified when a new config read fails with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- metrics: test1: type: value query: 0: TEST1 """ ) pgmon.read_config(f"{tmpdirname}/config.yml") # Just make sure the config was read self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test reading a nonexistant config file with tempfile.TemporaryDirectory() as tmpdirname: self.assertRaises( FileNotFoundError, pgmon.read_config, f"{tmpdirname}/missing.yml" ) # Test reading an invalid config file with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """[default] This looks a lot like an ini file to me Or maybe a TOML? """ ) self.assertRaises( pgmon.ConfigError, pgmon.read_config, f"{tmpdirname}/config.yml" ) # Test reading a config that includes an invalid file with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- dbuser: evil metrics: test1: type: value query: 0: EVIL1 include: - missing_file.yml """ ) self.assertRaises( FileNotFoundError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test invalid log level with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- log_level: noisy dbuser: evil metrics: test1: type: value query: 0: EVIL1 """ ) self.assertRaises( pgmon.ConfigError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test invalid query return type with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- dbuser: evil metrics: test1: type: lots_of_data query: 0: EVIL1 """ ) self.assertRaises( pgmon.ConfigError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test invalid query dict type with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- dbuser: evil metrics: test1: type: lots_of_data query: EVIL1 """ ) self.assertRaises( pgmon.ConfigError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test incomplete metric: missing type with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- dbuser: evil metrics: test1: query: 0: EVIL1 """ ) self.assertRaises( pgmon.ConfigError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test incomplete metric: missing queries with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- dbuser: evil metrics: test1: type: value """ ) self.assertRaises( pgmon.ConfigError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test incomplete metric: empty queries with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- dbuser: evil metrics: test1: type: value query: {} """ ) self.assertRaises( pgmon.ConfigError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test incomplete metric: query dict is None with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- dbuser: evil metrics: test1: type: value query: """ ) self.assertRaises( pgmon.ConfigError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test reading a config with no metrics with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- dbuser: evil """ ) self.assertRaises( pgmon.ConfigError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test reading a query defined in a file but the file is missing with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- dbuser: evil metrics: test1: type: value query: 0: file:missing.sql """ ) self.assertRaises( FileNotFoundError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") # Test invalid query versions with tempfile.TemporaryDirectory() as tmpdirname: with open(f"{tmpdirname}/config.yml", "w") as f: f.write( """--- dbuser: evil metrics: test1: type: value query: default: EVIL1 """ ) self.assertRaises( pgmon.ConfigError, pgmon.read_config, f"{tmpdirname}/config.yml" ) self.assertEqual(pgmon.config["dbuser"], "postgres") self.assertEqual(pgmon.config["metrics"]["test1"]["query"][0], "TEST1") def test_version_num_to_release(self): self.assertEqual(pgmon.version_num_to_release(90602), 9.6) self.assertEqual(pgmon.version_num_to_release(130002), 13) def test_parse_version_rss(self): rss = """ PostgreSQL latest versionshttps://www.postgresql.org/PostgreSQL latest versionsen-usThu, 08 May 2025 00:00:00 +000017.5 https://www.postgresql.org/docs/17/release-17-5.html17.5 is the latest release in the 17 series. Thu, 08 May 2025 00:00:00 +0000https://www.postgresql.org/docs/17/release-17-5.html16.9 https://www.postgresql.org/docs/16/release-16-9.html16.9 is the latest release in the 16 series. Thu, 08 May 2025 00:00:00 +0000https://www.postgresql.org/docs/16/release-16-9.html15.13 https://www.postgresql.org/docs/15/release-15-13.html15.13 is the latest release in the 15 series. Thu, 08 May 2025 00:00:00 +0000https://www.postgresql.org/docs/15/release-15-13.html14.18 https://www.postgresql.org/docs/14/release-14-18.html14.18 is the latest release in the 14 series. Thu, 08 May 2025 00:00:00 +0000https://www.postgresql.org/docs/14/release-14-18.html13.21 https://www.postgresql.org/docs/13/release-13-21.html13.21 is the latest release in the 13 series. Thu, 08 May 2025 00:00:00 +0000https://www.postgresql.org/docs/13/release-13-21.html12.22 https://www.postgresql.org/docs/12/release-12-22.html12.22 is the latest release in the 12 series. This version is unsupported! Thu, 21 Nov 2024 00:00:00 +0000https://www.postgresql.org/docs/12/release-12-22.html11.22 https://www.postgresql.org/docs/11/release-11-22.html11.22 is the latest release in the 11 series. This version is unsupported! Thu, 09 Nov 2023 00:00:00 +0000https://www.postgresql.org/docs/11/release-11-22.html10.23 https://www.postgresql.org/docs/10/release-10-23.html10.23 is the latest release in the 10 series. This version is unsupported! Thu, 10 Nov 2022 00:00:00 +0000https://www.postgresql.org/docs/10/release-10-23.html9.6.24 https://www.postgresql.org/docs/9.6/release-9-6-24.html9.6.24 is the latest release in the 9.6 series. This version is unsupported! Thu, 11 Nov 2021 00:00:00 +0000https://www.postgresql.org/docs/9.6/release-9-6-24.html9.5.25 https://www.postgresql.org/docs/9.5/release-9-5-25.html9.5.25 is the latest release in the 9.5 series. This version is unsupported! Thu, 11 Feb 2021 00:00:00 +0000https://www.postgresql.org/docs/9.5/release-9-5-25.html9.4.26 https://www.postgresql.org/docs/9.4/release-9-4-26.html9.4.26 is the latest release in the 9.4 series. This version is unsupported! Thu, 13 Feb 2020 00:00:00 +0000https://www.postgresql.org/docs/9.4/release-9-4-26.html9.3.25 https://www.postgresql.org/docs/9.3/release-9-3-25.html9.3.25 is the latest release in the 9.3 series. This version is unsupported! Thu, 08 Nov 2018 00:00:00 +0000https://www.postgresql.org/docs/9.3/release-9-3-25.html9.2.24 https://www.postgresql.org/docs/9.2/release-9-2-24.html9.2.24 is the latest release in the 9.2 series. This version is unsupported! Thu, 09 Nov 2017 00:00:00 +0000https://www.postgresql.org/docs/9.2/release-9-2-24.html9.1.24 https://www.postgresql.org/docs/9.1/release-9-1-24.html9.1.24 is the latest release in the 9.1 series. This version is unsupported! Thu, 27 Oct 2016 00:00:00 +0000https://www.postgresql.org/docs/9.1/release-9-1-24.html9.0.23 https://www.postgresql.org/docs/9.0/release-9-0-23.html9.0.23 is the latest release in the 9.0 series. This version is unsupported! Thu, 08 Oct 2015 00:00:00 +0000https://www.postgresql.org/docs/9.0/release-9-0-23.html8.4.22 https://www.postgresql.org/docs/8.4/release-8-4-22.html8.4.22 is the latest release in the 8.4 series. This version is unsupported! Thu, 24 Jul 2014 00:00:00 +0000https://www.postgresql.org/docs/8.4/release-8-4-22.html8.3.23 https://www.postgresql.org/docs/8.3/release-8-3-23.html8.3.23 is the latest release in the 8.3 series. This version is unsupported! Thu, 07 Feb 2013 00:00:00 +0000https://www.postgresql.org/docs/8.3/release-8-3-23.html8.2.23 https://www.postgresql.org/docs/8.2/release-8-2-23.html8.2.23 is the latest release in the 8.2 series. This version is unsupported! Mon, 05 Dec 2011 00:00:00 +0000https://www.postgresql.org/docs/8.2/release-8-2-23.html8.1.23 https://www.postgresql.org/docs/8.1/release.html8.1.23 is the latest release in the 8.1 series. This version is unsupported! Thu, 16 Dec 2010 00:00:00 +0000https://www.postgresql.org/docs/8.1/release.html8.0.26 https://www.postgresql.org/docs/8.0/release.html8.0.26 is the latest release in the 8.0 series. This version is unsupported! Mon, 04 Oct 2010 00:00:00 +0000https://www.postgresql.org/docs/8.0/release.html7.4.30 https://www.postgresql.org/docs/7.4/release.html7.4.30 is the latest release in the 7.4 series. This version is unsupported! Mon, 04 Oct 2010 00:00:00 +0000https://www.postgresql.org/docs/7.4/release.html7.3.21 https://www.postgresql.org/docs/7.3/release.html7.3.21 is the latest release in the 7.3 series. This version is unsupported! Mon, 07 Jan 2008 00:00:00 +0000https://www.postgresql.org/docs/7.3/release.html7.2.8 https://www.postgresql.org/docs/7.2/release.html7.2.8 is the latest release in the 7.2 series. This version is unsupported! Mon, 09 May 2005 00:00:00 +0000https://www.postgresql.org/docs/7.2/release.html7.1.3 https://www.postgresql.org/docs/7.1/release.html7.1.3 is the latest release in the 7.1 series. This version is unsupported! Fri, 17 Aug 2001 00:00:00 +0000https://www.postgresql.org/docs/7.1/release.html7.0.3 https://www.postgresql.org/docs/7.0/release.htm7.0.3 is the latest release in the 7.0 series. This version is unsupported! Sun, 12 Nov 2000 00:00:00 +0000https://www.postgresql.org/docs/7.0/release.htm6.5.3 https://www.postgresql.org/docs/6.5/release.htm6.5.3 is the latest release in the 6.5 series. This version is unsupported! Thu, 04 Nov 1999 00:00:00 +0000https://www.postgresql.org/docs/6.5/release.htm6.4.2 https://www.postgresql.org/docs/6.4/release.htm6.4.2 is the latest release in the 6.4 series. This version is unsupported! Sun, 03 Jan 1999 00:00:00 +0000https://www.postgresql.org/docs/6.4/release.htm6.3.2 https://www.postgresql.org/docs/6.3/c2701.htm6.3.2 is the latest release in the 6.3 series. This version is unsupported! Mon, 23 Feb 1998 00:00:00 +0000https://www.postgresql.org/docs/6.3/c2701.htm """ pgmon.parse_version_rss(rss, 13) self.assertEqual(pgmon.latest_version, 130021) self.assertTrue(pgmon.release_supported) pgmon.parse_version_rss(rss, 9.6) self.assertEqual(pgmon.latest_version, 90624) self.assertFalse(pgmon.release_supported) def test_get_latest_version(self): # Define a cluster version here so the test doesn't need a database pgmon.cluster_version_next_check = datetime.now() + timedelta(hours=1) pgmon.cluster_version = 90623 # Set up a default config pgmon.update_deep(pgmon.config, pgmon.default_config) # Make sure we can pull the RSS file (we assume the 9.6 series won't be getting any more updates) self.assertEqual(pgmon.get_latest_version(), 90624)