tests for django db backends utils from decimal import decimal rounded from django db import notsupportederror connection from django db backends utils import formatnumber splitidentifier splittznamedelta truncatename from django test import simpletestcase transactiontestcase skipifdbfeature skipunlessdbfeature class testutilssimpletestcase def testtruncatenameself self assertequaltruncatenamesometable 10 sometable self assertequaltruncatenamesomelongtable 10 somela38a self assertequaltruncatenamesomelongtable 10 3 someloa38 self assertequaltruncatenamesomelongtable somelongtable user table syntax self assertequal truncatename username sometable 10 username sometable self assertequal truncatename username somelongtable 10 username somela38a self assertequal truncatename username somelongtable 10 3 username someloa38 def testsplitidentifierself self assertequalsplitidentifiersometable sometable self assertequalsplitidentifier sometable sometable self assertequal splitidentifier namespace sometable namespace sometable self assertequal splitidentifier namespace sometable namespace sometable def testformatnumberself def equalvalue maxd places result self assertequalformatnumberdecimalvalue maxd places result equal0 12 3 0 000 equal0 12 8 0 00000000 equal1 12 9 1 000000000 equal0 00000000 12 8 0 00000000 equal0 000000004 12 8 0 00000000 equal0 000000008 12 8 0 00000001 equal0 000000000000000000999 10 8 0 00000000 equal0 1234567890 12 10 0 1234567890 equal0 1234567890 12 9 0 123456789 equal0 1234567890 12 8 0 12345679 equal0 1234567890 12 5 0 12346 equal0 1234567890 12 3 0 123 equal0 1234567890 12 1 0 1 equal0 1234567890 12 0 0 equal0 1234567890 none 0 0 equal1234567890 1234567890 none 0 1234567890 equal1234567890 1234567890 none 2 1234567890 12 equal0 1234 5 none 0 1234 equal123 12 5 none 123 12 with self assertraisesrounded equal0 1234567890 5 none 0 12346 with self assertraisesrounded equal1234567890 1234 5 none 1234600000 def testsplittznamedeltaself tests asiaustnera asiaustnera none none asiaustnera asiaustnera none none asiaustnera02 00 asiaustnera 02 00 asiaustnera05 00 asiaustnera 05 00 americacoralharbour01 00 americacoralharbour 01 00 americacoralharbour02 30 americacoralharbour 02 30 utc15 00 utc 15 00 utc04 43 utc 04 43 utc utc none none utc1 utc1 none none for tzname expected in tests with self subtesttznametzname self assertequalsplittznamedeltatzname expected class cursorwrapperteststransactiontestcase availableapps def testprocedureself proceduresql params paramtypes kparamsnone with connection cursor as cursor cursor executeproceduresql use a new cursor because in mysql a procedure can t be used in the same cursor in which it was created with connection cursor as cursor cursor callproctestprocedure params kparams with connection schemaeditor as editor editor removeproceduretestprocedure paramtypes skipunlessdbfeaturecreatetestprocedurewithoutparamssql def testcallprocwithoutparamsself self testprocedure connection features createtestprocedurewithoutparamssql skipunlessdbfeaturecreatetestprocedurewithintparamsql def testcallprocwithintparamsself self testprocedure connection features createtestprocedurewithintparamsql 1 integer skipunlessdbfeature createtestprocedurewithintparamsql supportscallprockwargs def testcallprockparamsself self testprocedure connection features createtestprocedurewithintparamsql integer pi 1 skipifdbfeaturesupportscallprockwargs def testunsupportedcallprockparamsraiseserrorself msg keyword parameters for callproc are not supported on this database backend with self assertraisesmessagenotsupportederror msg with connection cursor as cursor cursor callproctestprocedure pi 1 user table syntax use a new cursor because in mysql a procedure can t be used in the same cursor in which it was created
from decimal import Decimal, Rounded

from django.db import NotSupportedError, connection
from django.db.backends.utils import (
    format_number,
    split_identifier,
    split_tzname_delta,
    truncate_name,
)
from django.test import (
    SimpleTestCase,
    TransactionTestCase,
    skipIfDBFeature,
    skipUnlessDBFeature,
)


class TestUtils(SimpleTestCase):
    def test_truncate_name(self):
        self.assertEqual(truncate_name("some_table", 10), "some_table")
        self.assertEqual(truncate_name("some_long_table", 10), "some_la38a")
        self.assertEqual(truncate_name("some_long_table", 10, 3), "some_loa38")
        self.assertEqual(truncate_name("some_long_table"), "some_long_table")
        
        self.assertEqual(
            truncate_name('username"."some_table', 10), 'username"."some_table'
        )
        self.assertEqual(
            truncate_name('username"."some_long_table', 10), 'username"."some_la38a'
        )
        self.assertEqual(
            truncate_name('username"."some_long_table', 10, 3), 'username"."some_loa38'
        )

    def test_split_identifier(self):
        self.assertEqual(split_identifier("some_table"), ("", "some_table"))
        self.assertEqual(split_identifier('"some_table"'), ("", "some_table"))
        self.assertEqual(
            split_identifier('namespace"."some_table'), ("namespace", "some_table")
        )
        self.assertEqual(
            split_identifier('"namespace"."some_table"'), ("namespace", "some_table")
        )

    def test_format_number(self):
        def equal(value, max_d, places, result):
            self.assertEqual(format_number(Decimal(value), max_d, places), result)

        equal("0", 12, 3, "0.000")
        equal("0", 12, 8, "0.00000000")
        equal("1", 12, 9, "1.000000000")
        equal("0.00000000", 12, 8, "0.00000000")
        equal("0.000000004", 12, 8, "0.00000000")
        equal("0.000000008", 12, 8, "0.00000001")
        equal("0.000000000000000000999", 10, 8, "0.00000000")
        equal("0.1234567890", 12, 10, "0.1234567890")
        equal("0.1234567890", 12, 9, "0.123456789")
        equal("0.1234567890", 12, 8, "0.12345679")
        equal("0.1234567890", 12, 5, "0.12346")
        equal("0.1234567890", 12, 3, "0.123")
        equal("0.1234567890", 12, 1, "0.1")
        equal("0.1234567890", 12, 0, "0")
        equal("0.1234567890", None, 0, "0")
        equal("1234567890.1234567890", None, 0, "1234567890")
        equal("1234567890.1234567890", None, 2, "1234567890.12")
        equal("0.1234", 5, None, "0.1234")
        equal("123.12", 5, None, "123.12")

        with self.assertRaises(Rounded):
            equal("0.1234567890", 5, None, "0.12346")
        with self.assertRaises(Rounded):
            equal("1234567890.1234", 5, None, "1234600000")

    def test_split_tzname_delta(self):
        tests = [
            ("Asia/Ust+Nera", ("Asia/Ust+Nera", None, None)),
            ("Asia/Ust-Nera", ("Asia/Ust-Nera", None, None)),
            ("Asia/Ust+Nera-02:00", ("Asia/Ust+Nera", "-", "02:00")),
            ("Asia/Ust-Nera+05:00", ("Asia/Ust-Nera", "+", "05:00")),
            ("America/Coral_Harbour-01:00", ("America/Coral_Harbour", "-", "01:00")),
            ("America/Coral_Harbour+02:30", ("America/Coral_Harbour", "+", "02:30")),
            ("UTC+15:00", ("UTC", "+", "15:00")),
            ("UTC-04:43", ("UTC", "-", "04:43")),
            ("UTC", ("UTC", None, None)),
            ("UTC+1", ("UTC+1", None, None)),
        ]
        for tzname, expected in tests:
            with self.subTest(tzname=tzname):
                self.assertEqual(split_tzname_delta(tzname), expected)


class CursorWrapperTests(TransactionTestCase):
    available_apps = []

    def _test_procedure(self, procedure_sql, params, param_types, kparams=None):
        with connection.cursor() as cursor:
            cursor.execute(procedure_sql)
        
        
        with connection.cursor() as cursor:
            cursor.callproc("test_procedure", params, kparams)
        with connection.schema_editor() as editor:
            editor.remove_procedure("test_procedure", param_types)

    @skipUnlessDBFeature("create_test_procedure_without_params_sql")
    def test_callproc_without_params(self):
        self._test_procedure(
            connection.features.create_test_procedure_without_params_sql, [], []
        )

    @skipUnlessDBFeature("create_test_procedure_with_int_param_sql")
    def test_callproc_with_int_params(self):
        self._test_procedure(
            connection.features.create_test_procedure_with_int_param_sql,
            [1],
            ["INTEGER"],
        )

    @skipUnlessDBFeature(
        "create_test_procedure_with_int_param_sql", "supports_callproc_kwargs"
    )
    def test_callproc_kparams(self):
        self._test_procedure(
            connection.features.create_test_procedure_with_int_param_sql,
            [],
            ["INTEGER"],
            {"P_I": 1},
        )

    @skipIfDBFeature("supports_callproc_kwargs")
    def test_unsupported_callproc_kparams_raises_error(self):
        msg = (
            "Keyword parameters for callproc are not supported on this database "
            "backend."
        )
        with self.assertRaisesMessage(NotSupportedError, msg):
            with connection.cursor() as cursor:
                cursor.callproc("test_procedure", [], {"P_I": 1})