|
2 | 2 | import json |
3 | 3 | import pytest |
4 | 4 | from unittest.mock import Mock |
5 | | -from terminusdb_client.woqlquery.woql_query import WOQLQuery, Var |
| 5 | +from terminusdb_client.woqlquery.woql_query import WOQLQuery, Var, Doc |
6 | 6 | from terminusdb_client.errors import InterfaceError |
7 | 7 |
|
8 | 8 |
|
9 | 9 | class TestWOQLQueryCoverage: |
10 | 10 | """Test cases for uncovered lines in woql_query.py""" |
11 | 11 |
|
| 12 | + def test_doc_convert_and_to_dict(self): |
| 13 | + """Test Doc._convert and to_dict for various value types. |
| 14 | +
|
| 15 | + Covers conversion of primitives, None, lists, Vars, and dicts. |
| 16 | + """ |
| 17 | + # String |
| 18 | + d = Doc("hello") |
| 19 | + assert d.to_dict() == { |
| 20 | + "@type": "Value", |
| 21 | + "data": {"@type": "xsd:string", "@value": "hello"}, |
| 22 | + } |
| 23 | + |
| 24 | + # Boolean |
| 25 | + d = Doc(True) |
| 26 | + assert d.to_dict() == { |
| 27 | + "@type": "Value", |
| 28 | + "data": {"@type": "xsd:boolean", "@value": True}, |
| 29 | + } |
| 30 | + |
| 31 | + # Integer |
| 32 | + d = Doc(42) |
| 33 | + assert d.to_dict() == { |
| 34 | + "@type": "Value", |
| 35 | + "data": {"@type": "xsd:integer", "@value": 42}, |
| 36 | + } |
| 37 | + |
| 38 | + # Float (stored as decimal) |
| 39 | + d = Doc(3.14) |
| 40 | + assert d.to_dict() == { |
| 41 | + "@type": "Value", |
| 42 | + "data": {"@type": "xsd:decimal", "@value": 3.14}, |
| 43 | + } |
| 44 | + |
| 45 | + # None |
| 46 | + d = Doc({"maybe": None}) |
| 47 | + encoded = d.to_dict() |
| 48 | + assert encoded["@type"] == "Value" |
| 49 | + dictionary = encoded["dictionary"] |
| 50 | + assert dictionary["@type"] == "DictionaryTemplate" |
| 51 | + # The value for the None field should be encoded as None |
| 52 | + field_pair = dictionary["data"][0] |
| 53 | + assert field_pair["field"] == "maybe" |
| 54 | + assert field_pair["value"] is None |
| 55 | + |
| 56 | + # List with mixed values |
| 57 | + d = Doc(["a", 1, False]) |
| 58 | + encoded = d.to_dict() |
| 59 | + assert encoded["@type"] == "Value" |
| 60 | + assert "list" in encoded |
| 61 | + assert len(encoded["list"]) == 3 |
| 62 | + |
| 63 | + # Var instance |
| 64 | + v = Var("vname") |
| 65 | + d = Doc(v) |
| 66 | + assert d.to_dict() == {"@type": "Value", "variable": "vname"} |
| 67 | + |
| 68 | + # Nested dict |
| 69 | + d = Doc({"outer": {"inner": 5}}) |
| 70 | + encoded = d.to_dict() |
| 71 | + assert encoded["@type"] == "Value" |
| 72 | + dict_tmpl = encoded["dictionary"] |
| 73 | + assert dict_tmpl["@type"] == "DictionaryTemplate" |
| 74 | + outer_pair = dict_tmpl["data"][0] |
| 75 | + assert outer_pair["field"] == "outer" |
| 76 | + inner_value = outer_pair["value"] |
| 77 | + assert inner_value["@type"] == "Value" |
| 78 | + inner_dict_tmpl = inner_value["dictionary"] |
| 79 | + assert inner_dict_tmpl["@type"] == "DictionaryTemplate" |
| 80 | + |
| 81 | + def test_doc_str_uses_original_dictionary(self): |
| 82 | + """Test Doc.__str__ returns the original dictionary string representation.""" |
| 83 | + payload = {"k": "v"} |
| 84 | + d = Doc(payload) |
| 85 | + assert str(d) == str(payload) |
| 86 | + |
| 87 | + def test_vars_helper_creates_var_instances(self): |
| 88 | + """Test that vars() creates Var instances with expected names.""" |
| 89 | + wq = WOQLQuery() |
| 90 | + v1, v2, v3 = wq.vars("a", "b", "c") |
| 91 | + assert isinstance(v1, Var) |
| 92 | + assert isinstance(v2, Var) |
| 93 | + assert isinstance(v3, Var) |
| 94 | + assert str(v1) == "a" |
| 95 | + assert str(v2) == "b" |
| 96 | + assert str(v3) == "c" |
| 97 | + |
| 98 | + def test_init_uses_short_name_mapping_and_aliases(self): |
| 99 | + """Test WOQLQuery initialisation sets context and alias methods.""" |
| 100 | + # Default init |
| 101 | + wq = WOQLQuery() |
| 102 | + # _vocab should be initialised from SHORT_NAME_MAPPING (at least check a few keys) |
| 103 | + for key in ("type", "string", "boolean"): |
| 104 | + assert key in wq._vocab |
| 105 | + |
| 106 | + # Aliases should delegate to the expected methods (bound methods are not identical |
| 107 | + # objects on each access, so compare underlying functions) |
| 108 | + assert wq.update.__func__ is wq.update_document.__func__ |
| 109 | + assert wq.delete.__func__ is wq.delete_document.__func__ |
| 110 | + assert wq.read.__func__ is wq.read_document.__func__ |
| 111 | + assert wq.insert.__func__ is wq.insert_document.__func__ |
| 112 | + assert wq.optional.__func__ is wq.opt.__func__ |
| 113 | + assert wq.idgenerator.__func__ is wq.idgen.__func__ |
| 114 | + assert wq.concatenate.__func__ is wq.concat.__func__ |
| 115 | + assert wq.typecast.__func__ is wq.cast.__func__ |
| 116 | + |
| 117 | + # Initial query/cursor state when passing a pre-existing query dict |
| 118 | + initial = {"@type": "And", "and": []} |
| 119 | + wq2 = WOQLQuery(query=initial) |
| 120 | + # _query should be the same object, and _cursor should reference it |
| 121 | + assert wq2._query is initial |
| 122 | + assert wq2._cursor is initial |
| 123 | + |
12 | 124 | def test_varj_method(self): |
13 | 125 | """Test _varj method""" |
14 | 126 | wq = WOQLQuery() |
|
0 commit comments