2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Unit tests for pgmon
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# pylint: disable=too-many-lines
|
|
|
|
|
|
2025-01-03 07:16:40 +00:00
|
|
|
import unittest
|
|
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
import os
|
2025-01-08 07:39:20 +00:00
|
|
|
from datetime import datetime, timedelta
|
2025-01-08 22:01:11 +00:00
|
|
|
import tempfile
|
|
|
|
|
|
|
|
|
|
import logging
|
2025-01-08 07:39:20 +00:00
|
|
|
|
2025-07-03 05:47:06 +00:00
|
|
|
from decimal import Decimal
|
2025-07-03 06:08:45 +00:00
|
|
|
import json
|
2025-07-03 05:47:06 +00:00
|
|
|
|
2025-01-03 07:16:40 +00:00
|
|
|
import pgmon
|
|
|
|
|
|
2025-01-08 22:01:11 +00:00
|
|
|
# Silence most logging output
|
|
|
|
|
logging.disable(logging.CRITICAL)
|
|
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
VERSIONS_RSS = """
|
2025-06-02 16:39:34 +00:00
|
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>PostgreSQL latest versions</title><link>https://www.postgresql.org/</link><description>PostgreSQL latest versions</description><atom:link href="https://www.postgresql.org/versions.rss" rel="self"/><language>en-us</language><lastBuildDate>Thu, 08 May 2025 00:00:00 +0000</lastBuildDate><item><title>17.5
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/17/release-17-5.html</link><description>17.5 is the latest release in the 17 series.
|
|
|
|
|
|
|
|
|
|
</description><pubDate>Thu, 08 May 2025 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/17/release-17-5.html</guid></item><item><title>16.9
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/16/release-16-9.html</link><description>16.9 is the latest release in the 16 series.
|
|
|
|
|
|
|
|
|
|
</description><pubDate>Thu, 08 May 2025 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/16/release-16-9.html</guid></item><item><title>15.13
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/15/release-15-13.html</link><description>15.13 is the latest release in the 15 series.
|
|
|
|
|
|
|
|
|
|
</description><pubDate>Thu, 08 May 2025 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/15/release-15-13.html</guid></item><item><title>14.18
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/14/release-14-18.html</link><description>14.18 is the latest release in the 14 series.
|
|
|
|
|
|
|
|
|
|
</description><pubDate>Thu, 08 May 2025 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/14/release-14-18.html</guid></item><item><title>13.21
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/13/release-13-21.html</link><description>13.21 is the latest release in the 13 series.
|
|
|
|
|
|
|
|
|
|
</description><pubDate>Thu, 08 May 2025 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/13/release-13-21.html</guid></item><item><title>12.22
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/12/release-12-22.html</link><description>12.22 is the latest release in the 12 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 21 Nov 2024 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/12/release-12-22.html</guid></item><item><title>11.22
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/11/release-11-22.html</link><description>11.22 is the latest release in the 11 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 09 Nov 2023 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/11/release-11-22.html</guid></item><item><title>10.23
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/10/release-10-23.html</link><description>10.23 is the latest release in the 10 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 10 Nov 2022 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/10/release-10-23.html</guid></item><item><title>9.6.24
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/9.6/release-9-6-24.html</link><description>9.6.24 is the latest release in the 9.6 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 11 Nov 2021 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/9.6/release-9-6-24.html</guid></item><item><title>9.5.25
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/9.5/release-9-5-25.html</link><description>9.5.25 is the latest release in the 9.5 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 11 Feb 2021 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/9.5/release-9-5-25.html</guid></item><item><title>9.4.26
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/9.4/release-9-4-26.html</link><description>9.4.26 is the latest release in the 9.4 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 13 Feb 2020 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/9.4/release-9-4-26.html</guid></item><item><title>9.3.25
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/9.3/release-9-3-25.html</link><description>9.3.25 is the latest release in the 9.3 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 08 Nov 2018 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/9.3/release-9-3-25.html</guid></item><item><title>9.2.24
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/9.2/release-9-2-24.html</link><description>9.2.24 is the latest release in the 9.2 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 09 Nov 2017 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/9.2/release-9-2-24.html</guid></item><item><title>9.1.24
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/9.1/release-9-1-24.html</link><description>9.1.24 is the latest release in the 9.1 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 27 Oct 2016 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/9.1/release-9-1-24.html</guid></item><item><title>9.0.23
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/9.0/release-9-0-23.html</link><description>9.0.23 is the latest release in the 9.0 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 08 Oct 2015 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/9.0/release-9-0-23.html</guid></item><item><title>8.4.22
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/8.4/release-8-4-22.html</link><description>8.4.22 is the latest release in the 8.4 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 24 Jul 2014 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/8.4/release-8-4-22.html</guid></item><item><title>8.3.23
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/8.3/release-8-3-23.html</link><description>8.3.23 is the latest release in the 8.3 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 07 Feb 2013 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/8.3/release-8-3-23.html</guid></item><item><title>8.2.23
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/8.2/release-8-2-23.html</link><description>8.2.23 is the latest release in the 8.2 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Mon, 05 Dec 2011 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/8.2/release-8-2-23.html</guid></item><item><title>8.1.23
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/8.1/release.html</link><description>8.1.23 is the latest release in the 8.1 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 16 Dec 2010 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/8.1/release.html</guid></item><item><title>8.0.26
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/8.0/release.html</link><description>8.0.26 is the latest release in the 8.0 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Mon, 04 Oct 2010 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/8.0/release.html</guid></item><item><title>7.4.30
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/7.4/release.html</link><description>7.4.30 is the latest release in the 7.4 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Mon, 04 Oct 2010 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/7.4/release.html</guid></item><item><title>7.3.21
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/7.3/release.html</link><description>7.3.21 is the latest release in the 7.3 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Mon, 07 Jan 2008 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/7.3/release.html</guid></item><item><title>7.2.8
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/7.2/release.html</link><description>7.2.8 is the latest release in the 7.2 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Mon, 09 May 2005 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/7.2/release.html</guid></item><item><title>7.1.3
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/7.1/release.html</link><description>7.1.3 is the latest release in the 7.1 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Fri, 17 Aug 2001 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/7.1/release.html</guid></item><item><title>7.0.3
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/7.0/release.htm</link><description>7.0.3 is the latest release in the 7.0 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Sun, 12 Nov 2000 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/7.0/release.htm</guid></item><item><title>6.5.3
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/6.5/release.htm</link><description>6.5.3 is the latest release in the 6.5 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Thu, 04 Nov 1999 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/6.5/release.htm</guid></item><item><title>6.4.2
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/6.4/release.htm</link><description>6.4.2 is the latest release in the 6.4 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Sun, 03 Jan 1999 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/6.4/release.htm</guid></item><item><title>6.3.2
|
|
|
|
|
</title><link>https://www.postgresql.org/docs/6.3/c2701.htm</link><description>6.3.2 is the latest release in the 6.3 series.
|
|
|
|
|
This version is unsupported!
|
|
|
|
|
</description><pubDate>Mon, 23 Feb 1998 00:00:00 +0000</pubDate><guid>https://www.postgresql.org/docs/6.3/c2701.htm</guid></item></channel></rss>
|
|
|
|
|
"""
|
|
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
class TestPgmonMethods(unittest.TestCase): # pylint: disable=too-many-public-methods
|
|
|
|
|
"""
|
|
|
|
|
Unit test class for pgmon
|
|
|
|
|
"""
|
|
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
##
|
|
|
|
|
# update_deep
|
|
|
|
|
##
|
|
|
|
|
def test_update_deep__empty_cases(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test various empty dictionary permutations
|
|
|
|
|
"""
|
2025-05-13 05:44:47 +00:00
|
|
|
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):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test adding/updating scalar values
|
|
|
|
|
"""
|
2025-05-13 05:44:47 +00:00
|
|
|
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):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test adding to lists
|
|
|
|
|
"""
|
2025-05-13 05:44:47 +00:00
|
|
|
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):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test adding to dictionaries
|
|
|
|
|
"""
|
2025-05-13 05:44:47 +00:00
|
|
|
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):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test mismatched types
|
|
|
|
|
"""
|
2025-05-13 05:44:47 +00:00
|
|
|
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)
|
|
|
|
|
|
2025-10-09 03:53:45 +00:00
|
|
|
# Nested mismatched types
|
|
|
|
|
d1 = {"foo": {"a": 7}}
|
|
|
|
|
d2 = {"foo": [1, 2]}
|
|
|
|
|
self.assertRaises(TypeError, pgmon.update_deep, d1, d2)
|
|
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
##
|
|
|
|
|
# get_pool
|
|
|
|
|
##
|
|
|
|
|
|
|
|
|
|
def test_get_pool__simple(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test getting a pool in a normal case
|
|
|
|
|
"""
|
|
|
|
|
pgmon.Context.config.update(pgmon.DEFAULT_CONFIG)
|
2025-05-13 05:44:47 +00:00
|
|
|
pool = pgmon.get_pool("postgres")
|
|
|
|
|
self.assertIsNotNone(pool)
|
|
|
|
|
|
|
|
|
|
def test_get_pool__unhappy(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test getting an unhappy database pool
|
|
|
|
|
"""
|
|
|
|
|
pgmon.Context.config.update(pgmon.DEFAULT_CONFIG)
|
|
|
|
|
pgmon.Context.unhappy_cooldown["postgres"] = datetime.now() + timedelta(60)
|
2025-05-13 05:44:47 +00:00
|
|
|
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):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test adding to an empty unhappy list
|
|
|
|
|
"""
|
|
|
|
|
pgmon.Context.config.update(pgmon.DEFAULT_CONFIG)
|
|
|
|
|
pgmon.Context.unhappy_cooldown = {}
|
2025-05-13 05:44:47 +00:00
|
|
|
pool = pgmon.get_pool("postgres")
|
|
|
|
|
pgmon.handle_connect_failure(pool)
|
2025-09-23 05:12:49 +00:00
|
|
|
self.assertGreater(pgmon.Context.unhappy_cooldown["postgres"], datetime.now())
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test adding another database
|
|
|
|
|
pool = pgmon.get_pool("template0")
|
|
|
|
|
pgmon.handle_connect_failure(pool)
|
2025-09-23 05:12:49 +00:00
|
|
|
self.assertGreater(pgmon.Context.unhappy_cooldown["postgres"], datetime.now())
|
|
|
|
|
self.assertGreater(pgmon.Context.unhappy_cooldown["template0"], datetime.now())
|
|
|
|
|
self.assertEqual(len(pgmon.Context.unhappy_cooldown), 2)
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
##
|
|
|
|
|
# get_query
|
|
|
|
|
##
|
|
|
|
|
|
|
|
|
|
def test_get_query__basic(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test getting a query with just a default version.
|
|
|
|
|
"""
|
2025-05-13 05:44:47 +00:00
|
|
|
metric = {"type": "value", "query": {0: "DEFAULT"}}
|
|
|
|
|
self.assertEqual(pgmon.get_query(metric, 100000), "DEFAULT")
|
|
|
|
|
|
|
|
|
|
def test_get_query__versions(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test getting queries when multiple versions are present.
|
|
|
|
|
"""
|
2025-05-13 05:44:47 +00:00
|
|
|
metric = {"type": "value", "query": {0: "DEFAULT", 110000: "NEW"}}
|
|
|
|
|
|
2025-06-02 16:39:34 +00:00
|
|
|
# Test getting the default version of a query with no lower bound and a newer
|
|
|
|
|
# version
|
2025-05-13 05:44:47 +00:00
|
|
|
self.assertEqual(pgmon.get_query(metric, 100000), "DEFAULT")
|
|
|
|
|
|
2025-06-02 16:39:34 +00:00
|
|
|
# Test getting the newer version of a query with no lower bound and a newer
|
|
|
|
|
# version for the newer version
|
2025-05-13 05:44:47 +00:00
|
|
|
self.assertEqual(pgmon.get_query(metric, 110000), "NEW")
|
|
|
|
|
|
2025-06-02 16:39:34 +00:00
|
|
|
# Test getting the newer version of a query with no lower bound and a newer
|
|
|
|
|
# version for an even newer version
|
2025-05-13 05:44:47 +00:00
|
|
|
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):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test trying to get a query that is not defined for the requested version.
|
|
|
|
|
"""
|
2025-05-13 05:44:47 +00:00
|
|
|
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):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test reading a simple config.
|
|
|
|
|
"""
|
|
|
|
|
pgmon.Context.config = {}
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test reading just a metric and using the defaults for everything else
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
# This is a comment!
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
0: TEST1
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.read_config(os.path.join(tmpdirname, "config.yml"))
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
self.assertEqual(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.Context.config["max_pool_size"], pgmon.DEFAULT_CONFIG["max_pool_size"]
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
self.assertEqual(pgmon.Context.config["dbuser"], pgmon.DEFAULT_CONFIG["dbuser"])
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.Context.config = {}
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
# Test reading a basic config
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
# 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
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.read_config(os.path.join(tmpdirname, "config.yml"))
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
self.assertEqual(pgmon.Context.config["dbuser"], "someone")
|
|
|
|
|
self.assertEqual(pgmon.Context.config["metrics"]["test1"]["type"], "value")
|
|
|
|
|
self.assertEqual(pgmon.Context.config["metrics"]["test1"]["query"][0], "TEST1")
|
|
|
|
|
self.assertEqual(pgmon.Context.config["metrics"]["test2"]["query"][0], "TEST2")
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
def test_read_config__include(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test including one config from another.
|
|
|
|
|
"""
|
|
|
|
|
pgmon.Context.config = {}
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-06-02 16:39:34 +00:00
|
|
|
# Test reading a config that includes other files (absolute and relative paths,
|
|
|
|
|
# multiple levels)
|
2025-05-13 05:44:47 +00:00
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
2025-09-23 05:12:49 +00:00
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
# 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
|
2025-09-23 05:12:49 +00:00
|
|
|
- {}/metrics.yml
|
|
|
|
|
""".format(
|
|
|
|
|
tmpdirname
|
|
|
|
|
)
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
|
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "dbsettings.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
2025-09-23 05:12:49 +00:00
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: someone
|
|
|
|
|
dbhost: localhost
|
|
|
|
|
dbport: 5555
|
|
|
|
|
dbname: template0
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "metrics.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
2025-09-23 05:12:49 +00:00
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
0: TEST1
|
|
|
|
|
test2:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
0: TEST2
|
|
|
|
|
include:
|
|
|
|
|
- more_metrics.yml
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "more_metrics.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
2025-09-23 05:12:49 +00:00
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
metrics:
|
|
|
|
|
test3:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
0: TEST3
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.read_config(os.path.join(tmpdirname, "config.yml"))
|
2025-05-13 05:44:47 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
self.assertEqual(pgmon.Context.config["max_idle_time"], 10)
|
|
|
|
|
self.assertEqual(pgmon.Context.config["dbuser"], "someone")
|
|
|
|
|
self.assertEqual(pgmon.Context.config["metrics"]["test1"]["query"][0], "TEST1")
|
|
|
|
|
self.assertEqual(pgmon.Context.config["metrics"]["test2"]["query"][0], "TEST2")
|
|
|
|
|
self.assertEqual(pgmon.Context.config["metrics"]["test3"]["query"][0], "TEST3")
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
def test_read_config__reload(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test reloading a config.
|
|
|
|
|
"""
|
|
|
|
|
pgmon.Context.config = {}
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test rereading a config to update an existing config
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
# 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
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.read_config(os.path.join(tmpdirname, "config.yml"))
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
# Just make sure the first config was read
|
2025-09-23 05:12:49 +00:00
|
|
|
self.assertEqual(len(pgmon.Context.config["metrics"]), 2)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
# This is a comment!
|
|
|
|
|
min_pool_size: 7
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
0: NEW1
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.read_config(os.path.join(tmpdirname, "config.yml"))
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
self.assertEqual(pgmon.Context.config["min_pool_size"], 7)
|
|
|
|
|
self.assertEqual(pgmon.Context.config["metrics"]["test1"]["query"][0], "NEW1")
|
|
|
|
|
self.assertEqual(len(pgmon.Context.config["metrics"]), 1)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
def test_read_config__query_file(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test reading a query definition from a separate file
|
|
|
|
|
"""
|
|
|
|
|
pgmon.Context.config = {}
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
# Read a config file that reads a query from a file
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
0: file:some_query.sql
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "some_query.sql"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write("This is a query")
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.read_config(os.path.join(tmpdirname, "config.yml"))
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
self.assertEqual(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.Context.config["metrics"]["test1"]["query"][0], "This is a query"
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
def init_invalid_config_test(self):
|
|
|
|
|
"""
|
|
|
|
|
Initialize an invalid config read test. Basically just set up a simple valid config in
|
|
|
|
|
order to confirm that an invalid read does not modify the live config.
|
|
|
|
|
"""
|
|
|
|
|
pgmon.Context.config = {}
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
# 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:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
0: TEST1
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.read_config(os.path.join(tmpdirname, "config.yml"))
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
# Just make sure the config was read
|
2025-09-23 05:12:49 +00:00
|
|
|
self.assertEqual(pgmon.Context.config["metrics"]["test1"]["query"][0], "TEST1")
|
|
|
|
|
|
|
|
|
|
def verify_invalid_config_test(self):
|
|
|
|
|
"""
|
|
|
|
|
Verify that an invalid read did not modify the live config.
|
|
|
|
|
"""
|
|
|
|
|
self.assertEqual(pgmon.Context.config["dbuser"], "postgres")
|
|
|
|
|
self.assertEqual(pgmon.Context.config["metrics"]["test1"]["query"][0], "TEST1")
|
|
|
|
|
|
|
|
|
|
def test_read_config__missing(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading a nonexistant config file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
# Test reading a nonexistant config file
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
FileNotFoundError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "missing.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-01-08 22:01:11 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__invalid(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading an invalid config file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
|
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
# Test reading an invalid config file
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""[default]
|
2025-01-08 22:01:11 +00:00
|
|
|
This looks a lot like an ini file to me
|
|
|
|
|
|
|
|
|
|
Or maybe a TOML?
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
|
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__invalid_include(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading an invalid config file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
|
|
|
|
|
2025-05-13 05:44:47 +00:00
|
|
|
# Test reading a config that includes an invalid file
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: evil
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
0: EVIL1
|
|
|
|
|
include:
|
|
|
|
|
- missing_file.yml
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
FileNotFoundError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__invalid_log_level(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading an invalid log level from a config file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test invalid log level
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
log_level: noisy
|
|
|
|
|
dbuser: evil
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
0: EVIL1
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__invalid_type(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading an invalid query result type form a config file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test invalid query return type
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: evil
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: lots_of_data
|
|
|
|
|
query:
|
|
|
|
|
0: EVIL1
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__invalid_query_dict(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading an invalid query definition structure type form a config file. In other words
|
|
|
|
|
what's supposed to be a dictionary of the form version => query, we give it something else.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test invalid query dict type
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: evil
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
2025-09-23 05:12:49 +00:00
|
|
|
type: row
|
2025-01-08 22:01:11 +00:00
|
|
|
query: EVIL1
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__missing_type(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading a metric with a missing result type from a config file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test incomplete metric: missing type
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: evil
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
query:
|
|
|
|
|
0: EVIL1
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__missing_queries(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading a metric with no queries from a config file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test incomplete metric: missing queries
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: evil
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__empty_query_dict(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading a fetric with an empty query dict from a config file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test incomplete metric: empty queries
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: evil
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query: {}
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__none_query_dict(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading a metric where the query dict is None from a config file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test incomplete metric: query dict is None
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: evil
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__missing_metrics(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading a config file with no metrics.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test reading a config with no metrics
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: evil
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__missing_query_file(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading a metric from a config file where the query definition cones from a missing
|
|
|
|
|
file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test reading a query defined in a file but the file is missing
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: evil
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
0: file:missing.sql
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
FileNotFoundError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
def test_read_config__invalid_version(self):
|
|
|
|
|
"""
|
|
|
|
|
Test reading a metric with an invalid PostgreSQL version from a config file.
|
|
|
|
|
"""
|
|
|
|
|
# Set up the test
|
|
|
|
|
self.init_invalid_config_test()
|
2025-05-13 05:44:47 +00:00
|
|
|
|
|
|
|
|
# Test invalid query versions
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
2025-09-23 05:12:49 +00:00
|
|
|
with open(
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"), "w", encoding="utf-8"
|
|
|
|
|
) as f:
|
2025-05-13 05:44:47 +00:00
|
|
|
f.write(
|
|
|
|
|
"""---
|
2025-01-08 22:01:11 +00:00
|
|
|
dbuser: evil
|
|
|
|
|
metrics:
|
|
|
|
|
test1:
|
|
|
|
|
type: value
|
|
|
|
|
query:
|
|
|
|
|
default: EVIL1
|
2025-05-13 05:44:47 +00:00
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.read_config,
|
|
|
|
|
os.path.join(tmpdirname, "config.yml"),
|
2025-05-13 05:44:47 +00:00
|
|
|
)
|
2025-09-23 05:12:49 +00:00
|
|
|
|
|
|
|
|
# Confirm nothing changed
|
|
|
|
|
self.verify_invalid_config_test()
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
|
# version_num
|
|
|
|
|
##
|
2025-06-02 07:39:52 +00:00
|
|
|
|
2025-06-02 16:39:34 +00:00
|
|
|
def test_version_num_to_release__valid(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test converting PostgreSQL versions before and after 10 when the numbering scheme changed.
|
|
|
|
|
"""
|
2025-06-02 07:39:52 +00:00
|
|
|
self.assertEqual(pgmon.version_num_to_release(90602), 9.6)
|
|
|
|
|
self.assertEqual(pgmon.version_num_to_release(130002), 13)
|
|
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
##
|
|
|
|
|
# parse_version_rss
|
|
|
|
|
##
|
2025-06-02 07:39:52 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
def test_parse_version_rss__supported(self):
|
|
|
|
|
"""
|
|
|
|
|
Test parsing a supported version from the RSS feed
|
|
|
|
|
"""
|
|
|
|
|
pgmon.parse_version_rss(VERSIONS_RSS, 13)
|
|
|
|
|
self.assertEqual(pgmon.Context.latest_version, 130021)
|
|
|
|
|
self.assertTrue(pgmon.Context.release_supported)
|
|
|
|
|
|
|
|
|
|
def test_parse_version_rss__unsupported(self):
|
|
|
|
|
"""
|
|
|
|
|
Test parsing an unsupported version from the RSS feed
|
|
|
|
|
"""
|
|
|
|
|
pgmon.parse_version_rss(VERSIONS_RSS, 9.6)
|
|
|
|
|
self.assertEqual(pgmon.Context.latest_version, 90624)
|
|
|
|
|
self.assertFalse(pgmon.Context.release_supported)
|
2025-06-02 07:39:52 +00:00
|
|
|
|
2025-06-02 16:39:34 +00:00
|
|
|
def test_parse_version_rss__missing(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test asking about versions that don't exist in the RSS feed
|
|
|
|
|
"""
|
2025-06-02 16:39:34 +00:00
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.LatestVersionCheckError, pgmon.parse_version_rss, VERSIONS_RSS, 9.7
|
2025-06-02 16:39:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertRaises(
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.LatestVersionCheckError, pgmon.parse_version_rss, VERSIONS_RSS, 99
|
2025-06-02 16:39:34 +00:00
|
|
|
)
|
|
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
##
|
|
|
|
|
# get_latest_version
|
|
|
|
|
##
|
|
|
|
|
|
2025-10-09 03:53:45 +00:00
|
|
|
def test_get_latest_version__basic(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test getting the latest version from the actual RSS feed
|
|
|
|
|
"""
|
2025-06-02 07:39:52 +00:00
|
|
|
# Define a cluster version here so the test doesn't need a database
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.Context.cluster_version_next_check = datetime.now() + timedelta(hours=1)
|
|
|
|
|
pgmon.Context.cluster_version = 90623
|
2025-06-02 07:39:52 +00:00
|
|
|
|
|
|
|
|
# Set up a default config
|
2025-09-23 05:12:49 +00:00
|
|
|
pgmon.update_deep(pgmon.Context.config, pgmon.DEFAULT_CONFIG)
|
2025-06-02 07:39:52 +00:00
|
|
|
|
2025-06-02 16:39:34 +00:00
|
|
|
# Make sure we can pull the RSS file (we assume the 9.6 series won't be getting
|
|
|
|
|
# any more updates)
|
2025-06-02 07:39:52 +00:00
|
|
|
self.assertEqual(pgmon.get_latest_version(), 90624)
|
2025-07-03 05:47:06 +00:00
|
|
|
|
2025-09-23 05:12:49 +00:00
|
|
|
##
|
|
|
|
|
# json_encode_special
|
|
|
|
|
##
|
|
|
|
|
|
2025-10-09 03:53:45 +00:00
|
|
|
def test_json_encode_special__basic(self):
|
2025-09-23 05:12:49 +00:00
|
|
|
"""
|
|
|
|
|
Test encoding Decimal types as JSON
|
|
|
|
|
"""
|
2025-07-03 05:47:06 +00:00
|
|
|
# Confirm that we're getting the right type
|
2025-07-14 05:58:08 +00:00
|
|
|
self.assertFalse(isinstance(Decimal("0.5"), float))
|
|
|
|
|
self.assertTrue(isinstance(pgmon.json_encode_special(Decimal("0.5")), float))
|
2025-07-03 05:47:06 +00:00
|
|
|
|
|
|
|
|
# Make sure we get sane values
|
2025-07-14 05:58:08 +00:00
|
|
|
self.assertEqual(pgmon.json_encode_special(Decimal("0.5")), 0.5)
|
|
|
|
|
self.assertEqual(pgmon.json_encode_special(Decimal("12")), 12.0)
|
2025-07-03 05:47:06 +00:00
|
|
|
|
|
|
|
|
# Make sure we can still fail for other types
|
2025-07-14 05:58:08 +00:00
|
|
|
self.assertRaises(TypeError, pgmon.json_encode_special, object)
|
2025-07-03 06:08:45 +00:00
|
|
|
|
|
|
|
|
# Make sure we can actually serialize a Decimal
|
2025-07-14 05:58:08 +00:00
|
|
|
self.assertEqual(
|
|
|
|
|
json.dumps(Decimal("2.5"), default=pgmon.json_encode_special), "2.5"
|
|
|
|
|
)
|
2025-10-09 03:53:45 +00:00
|
|
|
|
|
|
|
|
##
|
|
|
|
|
# json_encode_result
|
|
|
|
|
##
|
|
|
|
|
|
|
|
|
|
def test_json_encode_result__value(self):
|
|
|
|
|
"""
|
|
|
|
|
Test encoding a single value return type, valid inputs
|
|
|
|
|
"""
|
|
|
|
|
# Empty result
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("value", []), "")
|
|
|
|
|
|
|
|
|
|
# Single value
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("value", [{"id": 5}]), "5")
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("value", [{"id": "5"}]), "5")
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("value", [{"key": "word"}]), "word")
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("value", [{"id": Decimal(5)}]), "5")
|
|
|
|
|
|
|
|
|
|
def test_json_encode_result__row(self):
|
|
|
|
|
"""
|
|
|
|
|
Test encoding a row, valid inputs
|
|
|
|
|
"""
|
|
|
|
|
# Empty result
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("row", {}), "{}")
|
|
|
|
|
|
|
|
|
|
# Simple row
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
pgmon.json_encode_result("row", [{"id": 5, "foo": "bar"}]),
|
|
|
|
|
'{"id": 5, "foo": "bar"}',
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Empry row (not ever likely to be what you want ... but technically not invalid)
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("row", [{}]), "{}")
|
|
|
|
|
|
|
|
|
|
def test_json_encode_result__column(self):
|
|
|
|
|
"""
|
|
|
|
|
Test encoding a column, valid inputs
|
|
|
|
|
"""
|
|
|
|
|
# Empty result
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("column", []), "[]")
|
|
|
|
|
|
|
|
|
|
# Simple column
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("column", [{"id": 5}]), "[5]")
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
pgmon.json_encode_result("column", [{"id": 5}, {"id": 7}, {"id": 2}]),
|
|
|
|
|
"[5, 7, 2]",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_json_encode_result__set(self):
|
|
|
|
|
"""
|
|
|
|
|
Test encoding a set, valid inputs
|
|
|
|
|
"""
|
|
|
|
|
# Empty result
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("set", []), "[]")
|
|
|
|
|
|
|
|
|
|
# Simple column
|
|
|
|
|
self.assertEqual(pgmon.json_encode_result("set", [{"id": 5}]), '[{"id": 5}]')
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
pgmon.json_encode_result(
|
|
|
|
|
"set", [{"id": 5, "foo": "bar"}, {"id": 7, "foo": "baz"}]
|
|
|
|
|
),
|
|
|
|
|
'[{"id": 5, "foo": "bar"}, {"id": 7, "foo": "baz"}]',
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_json_encode_result__invalid_type(self):
|
|
|
|
|
"""
|
|
|
|
|
Test requesting an invalid return type
|
|
|
|
|
"""
|
|
|
|
|
# Make sure an empty list raises the error
|
|
|
|
|
self.assertRaises(pgmon.ConfigError, pgmon.json_encode_result, "foo", [])
|
|
|
|
|
|
|
|
|
|
# Make sure including data still raises the error
|
|
|
|
|
self.assertRaises(
|
|
|
|
|
pgmon.ConfigError,
|
|
|
|
|
pgmon.json_encode_result,
|
|
|
|
|
"foo",
|
|
|
|
|
[{"id": 5, "foo": "bar"}],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_json_encode_result__invalid_value(self):
|
|
|
|
|
"""
|
|
|
|
|
Test invalid data/type combinations for a value
|
|
|
|
|
"""
|
|
|
|
|
# Note: We should always get a lsit of dicts from psycopg using the RealDictCursor
|
|
|
|
|
# Empty row returned
|
|
|
|
|
self.assertRaises(
|
|
|
|
|
pgmon.InvalidDataError, pgmon.json_encode_result, "value", [{}]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_json_encode_result__invalid_row(self):
|
|
|
|
|
"""
|
|
|
|
|
Test invalid data/type combinations for a row
|
|
|
|
|
"""
|
|
|
|
|
# Note: We should always get a lsit of dicts from psycopg using the RealDictCursor
|
|
|
|
|
# Note: I can't at this point think of any sort of invalid result psycopg2 should
|
|
|
|
|
# produce for this return type.
|
|
|
|
|
# This is basically a place holder for now.
|
|
|
|
|
True # pylint: disable=pointless-statement
|
|
|
|
|
|
|
|
|
|
def test_json_encode_result__invalid_set(self):
|
|
|
|
|
"""
|
|
|
|
|
Test invalid data/type combinations for a set
|
|
|
|
|
"""
|
|
|
|
|
# Note: We should always get a lsit of dicts from psycopg using the RealDictCursor
|
|
|
|
|
# Note: I can't at this point think of any sort of invalid result psycopg2 should
|
|
|
|
|
# produce for this return type.
|
|
|
|
|
# This is basically a place holder for now.
|
|
|
|
|
True # pylint: disable=pointless-statement
|