Repository: mirumee/ariadne

Issue:
convert_camel_case_to_snake produces bad results in two cases
Currently converts as follows:

```
FOO_bar  ->  fo_o_bar    # would prefer foo_bar
FOO_BAR  ->  fo_o_bar    # would prefer foo_bar

Could be fixed by adding a line after: https://github.com/mirumee/ariadne/blob/main/ariadne/utils.py#L70

```python
            or (
                i < max_index
                and graphql_name[i] != c
                and graphql_name[i + 1] == lowered_name[i + 1]
                and graphql_name[ i + 1 ] != "_"        # <--- NEW LINE
            )
```

Thoughts?

---

UPDATE:  I just came across another bad conversion case at work:

```
S3_BUCKET  ->  s_3__bucket    # would prefer s3_bucket
```

If there's interest in this, I can work on a patch.  Otherwise, I'll just fix it for myself with a custom converter.

Hints from issue comments:
@rafalp: Please contribute a patch, I would love to include a fix in new release I am planning sometime before xmas.@ofer-pd: I don't think I'll be able to get anything done before xmas - got a ton of work that needs to be finished by end of year.

I'll jump on this as soon as I can get some breathing room at work.@A-Dudek: I created a fix for `foo_bar` examples. The changes will also fix the `S3_BUCKET` string, but the converted string will be `s_3_bucket `, because in Ariadne the underscore is added before and after numbers.

Solution patch:
diff --git a/ariadne/utils.py b/ariadne/utils.py
index 0e7e2c10..9034b75e 100644
--- a/ariadne/utils.py
+++ b/ariadne/utils.py
@@ -67,11 +67,16 @@ def convert_camel_case_to_snake(graphql_name: str) -> str:
                 i < max_index
                 and graphql_name[i] != c
                 and graphql_name[i + 1] == lowered_name[i + 1]
+                and graphql_name[i + 1] != "_"
             )
             # test134 -> test_134
             or (c.isdigit() and not graphql_name[i - 1].isdigit())
             # 134test -> 134_test
-            or (not c.isdigit() and graphql_name[i - 1].isdigit())
+            or (
+                not c.isdigit()
+                and graphql_name[i] != "_"
+                and graphql_name[i - 1].isdigit()
+            )
         ):
             python_name += "_"
         python_name += c


Test patch:
diff --git a/tests/test_camel_case_to_snake_case_convertion.py b/tests/test_camel_case_to_snake_case_convertion.py
index b55040da..808ccc5b 100644
--- a/tests/test_camel_case_to_snake_case_convertion.py
+++ b/tests/test_camel_case_to_snake_case_convertion.py
@@ -42,6 +42,18 @@ def test_no_underscore_added_if_previous_character_is_an_underscore():
     assert convert_camel_case_to_snake("test__complexName") == "test__complex_name"
 
 
+@pytest.mark.parametrize(
+    ("test_str", "result"),
+    [
+        ("FOO_bar", "foo_bar"),
+        ("FOO_BAR", "foo_bar"),
+        ("S3_BUCKET", "s_3_bucket"),
+    ],
+)
+def test_no_underscore_added_if_next_character_is_an_underscore(test_str, result):
+    assert convert_camel_case_to_snake(test_str) == result
+
+
 def test_no_underscore_added_if_previous_character_is_uppercase():
     assert convert_camel_case_to_snake("testWithUPPERPart") == "test_with_upper_part"