From ef1976a8a9de3b9308070a1d2856c70f288d152d Mon Sep 17 00:00:00 2001 From: lwasylow Date: Sat, 18 May 2019 00:39:58 +0100 Subject: [PATCH 01/24] Initial checkin. General skelet of code --- source/api/ut.pkb | 5 ++ source/api/ut.pks | 2 + .../create_synonyms_and_grants_for_public.sql | 2 + source/create_user_grants.sql | 1 + source/create_user_synonyms.sql | 1 + .../data_values/ut_data_value_json.tpb | 89 +++++++++++++++++++ .../data_values/ut_data_value_json.tps | 27 ++++++ source/expectations/matchers/ut_equal.tpb | 6 ++ source/expectations/matchers/ut_equal.tps | 1 + source/expectations/ut_expectation.tpb | 10 ++- source/expectations/ut_expectation.tps | 2 + source/expectations/ut_expectation_json.tpb | 61 +++++++++++++ source/expectations/ut_expectation_json.tps | 35 ++++++++ source/install.sql | 4 + test/install_ut3_user_tests.sql | 2 + .../expectations/test_expectations_json.pkb | 71 +++++++++++++++ .../expectations/test_expectations_json.pks | 23 +++++ 17 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 source/expectations/data_values/ut_data_value_json.tpb create mode 100644 source/expectations/data_values/ut_data_value_json.tps create mode 100644 source/expectations/ut_expectation_json.tpb create mode 100644 source/expectations/ut_expectation_json.tps create mode 100644 test/ut3_user/expectations/test_expectations_json.pkb create mode 100644 test/ut3_user/expectations/test_expectations_json.pks diff --git a/source/api/ut.pkb b/source/api/ut.pkb index 5e13dcc62..440fed258 100644 --- a/source/api/ut.pkb +++ b/source/api/ut.pkb @@ -93,6 +93,11 @@ create or replace package body ut is return ut_expectation(ut_data_value_dsinterval(a_actual), a_message); end; + function expect(a_actual in json_element_t , a_message varchar2 := null) return ut_expectation_json is + begin + return ut_expectation_json(ut_data_value_json(a_actual), a_message); + end; + procedure fail(a_message in varchar2) is begin ut_expectation_processor.report_failure(a_message); diff --git a/source/api/ut.pks b/source/api/ut.pks index 7d3b191b3..4a9c6995a 100644 --- a/source/api/ut.pks +++ b/source/api/ut.pks @@ -45,6 +45,8 @@ create or replace package ut authid current_user as function expect(a_actual in dsinterval_unconstrained, a_message varchar2 := null) return ut_expectation; + function expect(a_actual in json_element_t , a_message varchar2 := null) return ut_expectation_json; + procedure fail(a_message in varchar2); function run( diff --git a/source/create_synonyms_and_grants_for_public.sql b/source/create_synonyms_and_grants_for_public.sql index 609d78ccf..2cd553860 100644 --- a/source/create_synonyms_and_grants_for_public.sql +++ b/source/create_synonyms_and_grants_for_public.sql @@ -33,6 +33,7 @@ alter session set current_schema = &&ut3_owner; grant execute on &&ut3_owner..ut_expectation to public; grant execute on &&ut3_owner..ut_expectation_compound to public; +grant execute on &&ut3_owner..ut_expectation_json to public; grant execute on &&ut3_owner..ut_be_between to public; grant execute on &&ut3_owner..ut_be_empty to public; grant execute on &&ut3_owner..ut_be_false to public; @@ -113,6 +114,7 @@ prompt Creating synonyms for UTPLSQL objects in &&ut3_owner schema to PUBLIC create public synonym ut_expectation for &&ut3_owner..ut_expectation; create public synonym ut_expectation_compound for &&ut3_owner..ut_expectation_compound; +create public synonym ut_expectation_json for &&ut3_owner..ut_expectation_json; create public synonym be_between for &&ut3_owner..be_between; create public synonym be_empty for &&ut3_owner..be_empty; diff --git a/source/create_user_grants.sql b/source/create_user_grants.sql index 7aa5deb39..fe1d3da29 100644 --- a/source/create_user_grants.sql +++ b/source/create_user_grants.sql @@ -53,6 +53,7 @@ alter session set current_schema = &&ut3_owner; grant execute on &&ut3_owner..ut_expectation to &ut3_user; grant execute on &&ut3_owner..ut_expectation_compound to &ut3_user; +grant execute on &&ut3_owner..ut_expectation_json to &ut3_user; grant execute on &&ut3_owner..ut_be_between to &ut3_user; grant execute on &&ut3_owner..ut_be_empty to &ut3_user; diff --git a/source/create_user_synonyms.sql b/source/create_user_synonyms.sql index cb2f7ab67..8fd687181 100644 --- a/source/create_user_synonyms.sql +++ b/source/create_user_synonyms.sql @@ -55,6 +55,7 @@ prompt Creating synonyms for UTPLSQL objects in &&ut3_owner schema to user &&ut3 create or replace synonym &ut3_user..ut_expectation for &&ut3_owner..ut_expectation; create or replace synonym &ut3_user..ut_expectation_compound for &&ut3_owner..ut_expectation_compound; +create or replace synonym &ut3_user..ut_expectation_json for &&ut3_owner..ut_expectation_json; create or replace synonym &ut3_user..be_between for &&ut3_owner..be_between; create or replace synonym &ut3_user..be_empty for &&ut3_owner..be_empty; diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb new file mode 100644 index 000000000..c7779300f --- /dev/null +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -0,0 +1,89 @@ +create or replace type body ut_data_value_json as + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + --IS JSON, JSON_EXISTS, JSON_TEXTCONTAINS + + constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result is + begin + + self.is_data_null := case when a_value is null then 1 when a_value.stringify = '{}' then 1 else 0 end; + self.data_value := case when a_value is null then null else a_value.to_clob end; + self.self_type := $$plsql_unit; + self.data_type := 'json'; + return; + end; + + overriding member function is_null return boolean is + begin + return (ut_utils.int_to_boolean(self.is_data_null)); + end; + + overriding member function to_string return varchar2 is + begin + return ut_utils.to_string(self.data_value); + end; + + overriding member function diff( a_other ut_data_value, a_match_options ut_matcher_options ) return varchar2 is + l_result clob; + l_results ut_utils.t_clob_tab := ut_utils.t_clob_tab(); + l_result_string varchar2(32767); + l_other ut_data_value_json; + l_self ut_data_value_json := self; + + l_act_keys ut_varchar2_list := ut_varchar2_list(); + l_exp_keys ut_varchar2_list := ut_varchar2_list(); + + c_max_rows integer := ut_utils.gc_diff_max_rows; + l_diff_id ut_compound_data_helper.t_hash; + l_diff_row_count integer; + l_row_diffs ut_compound_data_helper.tt_row_diffs; + l_message varchar2(32767); + + begin + if not a_other is of (ut_data_value_json) then + raise value_error; + end if; + l_other := treat(a_other as ut_data_value_json); + + l_result_string := ut_utils.to_string(l_result,null); + dbms_lob.freetemporary(l_result); + return l_result_string; + end; + + + overriding member function compare_implementation(a_other ut_data_value) return integer is + begin + return compare_implementation( a_other, null ); + end; + + member function compare_implementation(a_other ut_data_value,a_match_options ut_matcher_options) return integer is + l_result integer; + l_other ut_data_value_json; + begin + if a_other is of (ut_data_value_json) then + l_other := treat(a_other as ut_data_value_json); + select case when json_equal(self.data_value, l_other.data_value) then 0 else 1 end + into l_result + from dual; + end if; + + return l_result; + end; + +end; +/ diff --git a/source/expectations/data_values/ut_data_value_json.tps b/source/expectations/data_values/ut_data_value_json.tps new file mode 100644 index 000000000..c14c88198 --- /dev/null +++ b/source/expectations/data_values/ut_data_value_json.tps @@ -0,0 +1,27 @@ +create or replace type ut_data_value_json under ut_data_value( + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + is_data_null integer, + data_value clob, + constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result, + overriding member function is_null return boolean, + overriding member function to_string return varchar2, + overriding member function diff( a_other ut_data_value, a_match_options ut_matcher_options ) return varchar2, + overriding member function compare_implementation(a_other ut_data_value) return integer, + member function compare_implementation(a_other ut_data_value,a_match_options ut_matcher_options) return integer +) +/ diff --git a/source/expectations/matchers/ut_equal.tpb b/source/expectations/matchers/ut_equal.tpb index a666caaa3..6fb85532f 100644 --- a/source/expectations/matchers/ut_equal.tpb +++ b/source/expectations/matchers/ut_equal.tpb @@ -152,6 +152,12 @@ create or replace type body ut_equal as return; end; + constructor function ut_equal(self in out nocopy ut_equal, a_expected json_element_t, a_nulls_are_equal boolean := null) return self as result is + begin + init(ut_data_value_json(a_expected), a_nulls_are_equal); + return; + end; + member function include(a_items varchar2) return ut_equal is l_result ut_equal := self; begin diff --git a/source/expectations/matchers/ut_equal.tps b/source/expectations/matchers/ut_equal.tps index 68997fbcc..6158a6fd3 100644 --- a/source/expectations/matchers/ut_equal.tps +++ b/source/expectations/matchers/ut_equal.tps @@ -41,6 +41,7 @@ create or replace type ut_equal force under ut_comparison_matcher( constructor function ut_equal(self in out nocopy ut_equal, a_expected varchar2, a_nulls_are_equal boolean := null) return self as result, constructor function ut_equal(self in out nocopy ut_equal, a_expected yminterval_unconstrained, a_nulls_are_equal boolean := null) return self as result, constructor function ut_equal(self in out nocopy ut_equal, a_expected dsinterval_unconstrained, a_nulls_are_equal boolean := null) return self as result, + constructor function ut_equal(self in out nocopy ut_equal, a_expected json_element_t, a_nulls_are_equal boolean := null) return self as result, member function include(a_items varchar2) return ut_equal, member function include(a_items ut_varchar2_list) return ut_equal, member function exclude(a_items varchar2) return ut_equal, diff --git a/source/expectations/ut_expectation.tpb b/source/expectations/ut_expectation.tpb index 54de82b47..a94dadbc7 100644 --- a/source/expectations/ut_expectation.tpb +++ b/source/expectations/ut_expectation.tpb @@ -186,6 +186,10 @@ create or replace type body ut_expectation as self.to_( ut_equal(a_expected, a_nulls_are_equal) ); end; + member procedure to_equal(self in ut_expectation, a_expected json_element_t, a_nulls_are_equal boolean := null) is + begin + self.to_( ut_equal(a_expected, a_nulls_are_equal) ); + end; member procedure not_to_equal(self in ut_expectation, a_expected anydata, a_nulls_are_equal boolean := null) is begin @@ -288,6 +292,10 @@ create or replace type body ut_expectation as self.not_to( ut_equal(a_expected, a_nulls_are_equal) ); end; + member procedure not_to_equal(self in ut_expectation, a_expected json_element_t, a_nulls_are_equal boolean := null) is + begin + self.not_to( ut_equal(a_expected, a_nulls_are_equal) ); + end; member procedure to_be_like(self in ut_expectation, a_mask in varchar2, a_escape_char in varchar2 := null) is begin @@ -700,6 +708,6 @@ create or replace type body ut_expectation as begin self.not_to( ut_contain(a_expected).negated() ); end; - + end; / diff --git a/source/expectations/ut_expectation.tps b/source/expectations/ut_expectation.tps index eae74202e..79d81f106 100644 --- a/source/expectations/ut_expectation.tps +++ b/source/expectations/ut_expectation.tps @@ -51,6 +51,7 @@ create or replace type ut_expectation authid current_user as object( member procedure to_equal(self in ut_expectation, a_expected varchar2, a_nulls_are_equal boolean := null), member procedure to_equal(self in ut_expectation, a_expected yminterval_unconstrained, a_nulls_are_equal boolean := null), member procedure to_equal(self in ut_expectation, a_expected dsinterval_unconstrained, a_nulls_are_equal boolean := null), + member procedure to_equal(self in ut_expectation, a_expected json_element_t, a_nulls_are_equal boolean := null), member procedure not_to_equal(self in ut_expectation, a_expected anydata, a_nulls_are_equal boolean := null), member procedure not_to_equal(self in ut_expectation, a_expected anydata, a_exclude varchar2, a_nulls_are_equal boolean := null), @@ -69,6 +70,7 @@ create or replace type ut_expectation authid current_user as object( member procedure not_to_equal(self in ut_expectation, a_expected varchar2, a_nulls_are_equal boolean := null), member procedure not_to_equal(self in ut_expectation, a_expected yminterval_unconstrained, a_nulls_are_equal boolean := null), member procedure not_to_equal(self in ut_expectation, a_expected dsinterval_unconstrained, a_nulls_are_equal boolean := null), + member procedure not_to_equal(self in ut_expectation, a_expected json_element_t, a_nulls_are_equal boolean := null), member procedure to_be_like(self in ut_expectation, a_mask in varchar2, a_escape_char in varchar2 := null), diff --git a/source/expectations/ut_expectation_json.tpb b/source/expectations/ut_expectation_json.tpb new file mode 100644 index 000000000..cda83f691 --- /dev/null +++ b/source/expectations/ut_expectation_json.tpb @@ -0,0 +1,61 @@ +create or replace type body ut_expectation_json as + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + constructor function ut_expectation_json(self in out nocopy ut_expectation_json, a_actual_data ut_data_value, a_description varchar2) return self as result is + begin + self.actual_data := a_actual_data; + self.description := a_description; + return; + end; + + member procedure to_be_empty(self in ut_expectation_json) is + begin + self.to_( ut_be_empty() ); + end; + + member procedure not_to_be_empty(self in ut_expectation_json) is + begin + self.not_to( ut_be_empty() ); + end; + + member procedure to_have_count(self in ut_expectation_json, a_expected integer) is + begin + self.to_( ut_have_count(a_expected) ); + end; + + member procedure not_to_have_count(self in ut_expectation_json, a_expected integer) is + begin + self.not_to( ut_have_count(a_expected) ); + end; + + member function to_equal(a_expected json_element_t, a_nulls_are_equal boolean := null) return ut_expectation_json is + l_result ut_expectation_json := self; + begin + l_result.matcher := ut_equal(a_expected, a_nulls_are_equal); + return l_result; + end; + + member function not_to_equal(a_expected json_element_t, a_nulls_are_equal boolean := null) return ut_expectation_json is + l_result ut_expectation_json := self; + begin + l_result.matcher := ut_equal(a_expected, a_nulls_are_equal).negated(); + return l_result; + end; + +end; +/ diff --git a/source/expectations/ut_expectation_json.tps b/source/expectations/ut_expectation_json.tps new file mode 100644 index 000000000..b79268bd0 --- /dev/null +++ b/source/expectations/ut_expectation_json.tps @@ -0,0 +1,35 @@ +create or replace type ut_expectation_json force under ut_expectation( + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + matcher ut_matcher, + + constructor function ut_expectation_json(self in out nocopy ut_expectation_json, a_actual_data ut_data_value, a_description varchar2) return self as result, + + member procedure to_be_empty(self in ut_expectation_json), + member procedure not_to_be_empty(self in ut_expectation_json), + member procedure to_have_count(self in ut_expectation_json, a_expected integer), + member procedure not_to_have_count(self in ut_expectation_json, a_expected integer), + /* + member procedure to_contain_text(self in ut_expectation_json, a_expected clob), + member procedure not_to_contain_text(self in ut_expectation_json, a_expected clob), + member procedure to_exists(self in ut_expectation_json, a_expected clob), + member procedure not_to_exists(self in ut_expectation_json, a_expected clob), + */ + member function to_equal(a_expected json_element_t , a_nulls_are_equal boolean := null) return ut_expectation_json, + member function not_to_equal(a_expected json_element_t , a_nulls_are_equal boolean := null) return ut_expectation_json +) +/ diff --git a/source/install.sql b/source/install.sql index 728f4349f..863d4c7c6 100644 --- a/source/install.sql +++ b/source/install.sql @@ -211,6 +211,7 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'expectations/data_values/ut_data_value_yminterval.tps' @@install_component.sql 'expectations/data_values/ut_data_value_xmltype.tps' @@install_component.sql 'expectations/data_values/ut_compound_data_helper.pks' +@@install_component.sql 'expectations/data_values/ut_data_value_json.tps' @@install_component.sql 'expectations/matchers/ut_matcher.tps' @@install_component.sql 'expectations/matchers/ut_comparison_matcher.tps' @@install_component.sql 'expectations/matchers/ut_be_false.tps' @@ -232,6 +233,7 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'expectations/data_values/ut_cursor_column.tpb' @@install_component.sql 'expectations/data_values/ut_cursor_details.tpb' @@install_component.sql 'expectations/ut_expectation_compound.tps' +@@install_component.sql 'expectations/ut_expectation_json.tps' @@install_component.sql 'expectations/matchers/ut_matcher_options_items.tpb' @@install_component.sql 'expectations/matchers/ut_matcher_options.tpb' @@ -252,6 +254,7 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'expectations/data_values/ut_data_value_varchar2.tpb' @@install_component.sql 'expectations/data_values/ut_data_value_yminterval.tpb' @@install_component.sql 'expectations/data_values/ut_data_value_xmltype.tpb' +@@install_component.sql 'expectations/data_values/ut_data_value_json.tpb' @@install_component.sql 'expectations/matchers/ut_matcher.tpb' @@install_component.sql 'expectations/matchers/ut_comparison_matcher.tpb' @@install_component.sql 'expectations/matchers/ut_be_false.tpb' @@ -271,6 +274,7 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'expectations/matchers/ut_match.tpb' @@install_component.sql 'expectations/ut_expectation.tpb' @@install_component.sql 'expectations/ut_expectation_compound.tpb' +@@install_component.sql 'expectations/ut_expectation_json.tpb' @@install_component.sql 'expectations/data_values/ut_key_anyvalues.tpb' --core reporter diff --git a/test/install_ut3_user_tests.sql b/test/install_ut3_user_tests.sql index 67014f815..f58c9df37 100644 --- a/test/install_ut3_user_tests.sql +++ b/test/install_ut3_user_tests.sql @@ -23,6 +23,7 @@ prompt Install user tests @@ut3_user/expectations/test_matchers.pks @@ut3_user/expectations/test_expectation_anydata.pks @@ut3_user/expectations/test_expectations_cursor.pks +@@ut3_user/expectations/test_expectations_json.pks @@ut3_user/api/test_ut_runner.pks @@ut3_user/api/test_ut_run.pks @@ut3_user/reporters.pks @@ -61,6 +62,7 @@ set define off @@ut3_user/expectations/test_matchers.pkb @@ut3_user/expectations/test_expectation_anydata.pkb @@ut3_user/expectations/test_expectations_cursor.pkb +@@ut3_user/expectations/test_expectations_json.pkb @@ut3_user/api/test_ut_runner.pkb @@ut3_user/api/test_ut_run.pkb @@ut3_user/reporters.pkb diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb new file mode 100644 index 000000000..1f2b06048 --- /dev/null +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -0,0 +1,71 @@ +create or replace package body test_expectations_json is + + procedure cleanup_expectations is + begin + ut3_tester_helper.main_helper.clear_expectations( ); + end; + + procedure success_on_same_data + as + l_expected json_element_t; + l_actual json_element_t; + begin + -- Arrange + l_expected := json_element_t.parse('{"name1":"value1","name2":"value2"}'); + l_actual := json_element_t.parse('{"name1":"value1","name2":"value2"}'); + + --Act + ut3.ut.expect( l_actual ).to_equal( l_actual ); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure fail_on_diff_data + as + l_expected json_element_t; + l_actual json_element_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_expected := json_element_t.parse('{"name1":"value2","name2":"value2"}'); + l_actual := json_element_t.parse('{"name1":"value1","name2":"value2"}'); + + --Act + ut3.ut.expect( l_actual ).to_equal( l_expected ); + --Assert + l_expected_message := q'[%Actual: '{"name1":"value1","name2":"value2"}' (json) was expected to equal: '{"name1":"value2","name2":"value2"}' (json)%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure null_json_variable + as + l_expected json_object_t ; + begin + -- Arrange + l_expected := cast (null as JSON_OBJECT_T ); + + --Act + ut3.ut.expect( l_expected ).to_be_null; + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure null_json + as + l_expected json_element_t; + l_actual json_element_t; + begin + -- Arrange + l_expected := JSON_OBJECT_T(); + + --Act + ut3.ut.expect( l_expected ).to_be_null; + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + +end; +/ diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks new file mode 100644 index 000000000..641dbf179 --- /dev/null +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -0,0 +1,23 @@ +create or replace package test_expectations_json is + + --%suite(json expectations) + --%suitepath(utplsql.test_user.expectations) + + --%aftereach + procedure cleanup_expectations; + + + --%test(Gives success for identical data) + procedure success_on_same_data; + + --%test(Gives failure for different data) + procedure fail_on_diff_data; + + --%test( Json variable is null) + procedure null_json_variable; + + --%test(Json string is null) + procedure null_json; + +end; +/ From 9e7b6c4d3fa084e88f0ba2716d2cd099f519bc86 Mon Sep 17 00:00:00 2001 From: lwasylow Date: Sat, 18 May 2019 19:32:08 +0100 Subject: [PATCH 02/24] Adding json tree parsing. Enabling 12.2 --- .../data_values/ut_compound_data_helper.pkb | 65 ++++++ .../data_values/ut_compound_data_helper.pks | 30 ++- .../data_values/ut_data_value_json.tpb | 84 ++++++-- .../data_values/ut_data_value_json.tps | 10 +- .../expectations/data_values/ut_json_leaf.tpb | 31 +++ .../expectations/data_values/ut_json_leaf.tps | 41 ++++ .../data_values/ut_json_leaf_tab.tps | 19 ++ .../data_values/ut_json_tree_details.tpb | 185 ++++++++++++++++++ .../data_values/ut_json_tree_details.tps | 36 ++++ source/expectations/matchers/ut_equal.tpb | 5 + source/expectations/ut_expectation_json.tpb | 10 - source/expectations/ut_expectation_json.tps | 8 - source/install.sql | 5 + source/uninstall_objects.sql | 6 + .../expectations/test_expectations_json.pkb | 13 +- .../expectations/test_expectations_json.pks | 4 +- 16 files changed, 506 insertions(+), 46 deletions(-) create mode 100644 source/expectations/data_values/ut_json_leaf.tpb create mode 100644 source/expectations/data_values/ut_json_leaf.tps create mode 100644 source/expectations/data_values/ut_json_leaf_tab.tps create mode 100644 source/expectations/data_values/ut_json_tree_details.tpb create mode 100644 source/expectations/data_values/ut_json_tree_details.tps diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index bd259b47d..c8c819978 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -624,6 +624,71 @@ create or replace package body ut_compound_data_helper is end; end; + function get_json_diffs(a_act_json_data ut_json_leaf_tab,a_exp_json_data ut_json_leaf_tab) return tt_json_diff_tab is + l_result_diff tt_json_diff_tab := tt_json_diff_tab(); + begin + with differences as ( + select + case + when (a.element_name is null or e.element_name is null) then gc_json_missing + when a.json_type != e.json_type then gc_json_type + when (decode(a.element_value,e.element_value,1,0) = 0) then gc_json_notequal + else gc_json_unknown end as difference_type, + a.element_name as act_element_name, + a.element_value as act_element_value, + a.hierarchy_level as act_hierarchy_level, + a.json_type as act_json_type, + e.element_name as exp_element_name, + e.element_value as exp_element_value, + e.hierarchy_level as exp_hierarchy_level, + e.json_type as exp_json_type, + a.parent_name act_par_name, + e.parent_name exp_par_name + from table(a_act_json_data) a + full outer join table(a_exp_json_data) e + on decode(a.parent_name,e.parent_name,1,0)= 1 + and ( + case when a.parent_type = 'object' or e.parent_type = 'object' then + decode(a.element_name,e.element_name,1,0) + else 1 end = 1 + ) + and ( + case when a.parent_type = 'array' or e.parent_type = 'array' then + decode(a.index_position,e.index_position,1,0) + else 1 end = 1 + ) + and a.hierarchy_level = e.hierarchy_level + where (a.element_name is null or e.element_name is null) + or (a.json_type != e.json_type) + or (decode(a.element_value,e.element_value,1,0) = 0) + order by nvl(a.hierarchy_level,e.hierarchy_level),nvl(a.index_position,e.index_position) + ) + select difference_type, + act_element_name, + act_element_value, + act_json_type, + exp_element_name, + exp_element_value, + exp_json_type + bulk collect into l_result_diff + from differences a + where not exists ( select 1 from differences b where (a.act_par_name = b.act_element_name and a.act_hierarchy_level - 1 = b.act_hierarchy_level) + or (a.exp_par_name = b.exp_element_name and a.exp_hierarchy_level - 1 = b.exp_hierarchy_level)); + + return l_result_diff; + end; + + function get_json_diffs_type(a_diffs_all tt_json_diff_tab) return tt_json_diff_type_tab is + l_diffs_summary tt_json_diff_type_tab := tt_json_diff_type_tab(); + begin + select difference_type,count(1) + bulk collect into l_diffs_summary + from table(a_diffs_all) d + group by d.difference_type; + + return l_diffs_summary; + end; + begin g_anytype_name_map(dbms_types.typecode_date) := 'DATE'; g_anytype_name_map(dbms_types.typecode_number) := 'NUMBER'; diff --git a/source/expectations/data_values/ut_compound_data_helper.pks b/source/expectations/data_values/ut_compound_data_helper.pks index 1c00d2263..151bf063f 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pks +++ b/source/expectations/data_values/ut_compound_data_helper.pks @@ -19,6 +19,11 @@ create or replace package ut_compound_data_helper authid definer is gc_compare_unordered constant varchar2(10):='unordered'; gc_compare_normal constant varchar2(10):='normal'; + gc_json_missing constant varchar2(30) := 'missing properties'; + gc_json_type constant varchar2(30) := 'incorrect types'; + gc_json_notequal constant varchar2(30) := 'unequal values'; + gc_json_unknown constant varchar2(30) := 'unknown'; + type t_column_diffs is record( diff_type varchar2(1), expected_name varchar2(250), @@ -50,7 +55,26 @@ create or replace package ut_compound_data_helper authid definer is ); type t_diff_tab is table of t_diff_rec; - + + type t_json_diff_rec is record ( + difference_type varchar2(50), + act_element_name varchar2(4000), + act_element_value varchar2(4000), + act_json_type varchar2(4000), + exp_element_name varchar2(4000), + exp_element_value varchar2(4000), + exp_json_type varchar2(4000) + ); + + type tt_json_diff_tab is table of t_json_diff_rec; + + type t_json_diff_type_rec is record ( + difference_type varchar2(50), + no_of_occurence integer + ); + + type tt_json_diff_type_tab is table of t_json_diff_type_rec; + function get_columns_diff( a_expected ut_cursor_column_tab, a_actual ut_cursor_column_tab,a_order_enforced boolean := false ) return tt_column_diffs; @@ -99,5 +123,9 @@ create or replace package ut_compound_data_helper authid definer is */ function type_no_length ( a_type_name varchar2) return boolean; + function get_json_diffs(a_act_json_data ut_json_leaf_tab,a_exp_json_data ut_json_leaf_tab) return tt_json_diff_tab; + + function get_json_diffs_type(a_diffs_all tt_json_diff_tab) return tt_json_diff_type_tab; + end; / diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb index c7779300f..5c093efdc 100644 --- a/source/expectations/data_values/ut_data_value_json.tpb +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -19,12 +19,12 @@ create or replace type body ut_data_value_json as --IS JSON, JSON_EXISTS, JSON_TEXTCONTAINS constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result is - begin - - self.is_data_null := case when a_value is null then 1 when a_value.stringify = '{}' then 1 else 0 end; - self.data_value := case when a_value is null then null else a_value.to_clob end; - self.self_type := $$plsql_unit; - self.data_type := 'json'; + begin + self.is_data_null := case when a_value is null then 1 else 0 end; + self.data_value := case when a_value is null then null else a_value.to_clob end; + self.self_type := $$plsql_unit; + self.data_type := 'json'; + self.json_tree := ut_json_tree_details(a_value); return; end; @@ -33,6 +33,11 @@ create or replace type body ut_data_value_json as return (ut_utils.int_to_boolean(self.is_data_null)); end; + overriding member function is_empty return boolean is + begin + return self.data_value = '{}'; + end; + overriding member function to_string return varchar2 is begin return ut_utils.to_string(self.data_value); @@ -45,23 +50,64 @@ create or replace type body ut_data_value_json as l_other ut_data_value_json; l_self ut_data_value_json := self; - l_act_keys ut_varchar2_list := ut_varchar2_list(); - l_exp_keys ut_varchar2_list := ut_varchar2_list(); - c_max_rows integer := ut_utils.gc_diff_max_rows; - l_diff_id ut_compound_data_helper.t_hash; l_diff_row_count integer; - l_row_diffs ut_compound_data_helper.tt_row_diffs; + l_diffs ut_compound_data_helper.tt_json_diff_tab; l_message varchar2(32767); - + + function get_diff_by_type(a_diff ut_compound_data_helper.tt_json_diff_tab) return clob is + l_diff_summary ut_compound_data_helper.tt_json_diff_type_tab := ut_compound_data_helper.get_json_diffs_type(a_diff); + l_message varchar2(32767); + l_message_list ut_varchar2_list := ut_varchar2_list(); + begin + for i in 1..l_diff_summary.count loop + l_message_list.extend; + l_message_list(l_message_list.last) := l_diff_summary(i).no_of_occurence||' '||l_diff_summary(i).difference_type; + end loop; + return ut_utils.table_to_clob(l_message_list,','); + end; + + function get_json_diff_text (a_json_diff ut_compound_data_helper.t_json_diff_rec) return clob is + begin + return case + when a_json_diff.difference_type = ut_compound_data_helper.gc_json_missing and a_json_diff.act_element_name is not null + then 'Missing property '||a_json_diff.act_element_name + when a_json_diff.difference_type = ut_compound_data_helper.gc_json_missing and a_json_diff.exp_element_name is not null + then 'Extra property '||a_json_diff.exp_element_name + when a_json_diff.difference_type = ut_compound_data_helper.gc_json_type + then 'Actual type is '||a_json_diff.act_json_type||' was expected to be '||a_json_diff.exp_json_type + when a_json_diff.difference_type = ut_compound_data_helper.gc_json_notequal + then 'Actual value is '||a_json_diff.act_element_value||' was expected to be '||a_json_diff.exp_element_value + else 'Unknown' end; + end; + begin if not a_other is of (ut_data_value_json) then raise value_error; end if; l_other := treat(a_other as ut_data_value_json); + if not l_self.is_null and not l_other.is_null then + l_diffs := ut_compound_data_helper.get_json_diffs( + l_self.json_tree.json_tree_info, + l_other.json_tree.json_tree_info); + + l_message:= chr(10)||'Found: '||l_diffs.count|| case when l_diffs.count > 1 then ' differences.' else ' difference.' end||chr(10); + ut_utils.append_to_clob( l_result, l_message ); + l_message:= get_diff_by_type(l_diffs)||chr(10); + ut_utils.append_to_clob( l_result, l_message ); + + for i in 1..l_diffs.count loop + l_results.extend; + l_results(l_results.last) := get_json_diff_text(l_diffs(i)); + end loop; + ut_utils.append_to_clob(l_result, l_results); + + end if; + + l_result_string := ut_utils.to_string(l_result,null); - dbms_lob.freetemporary(l_result); + --dbms_lob.freetemporary(l_result); return l_result_string; end; @@ -77,13 +123,19 @@ create or replace type body ut_data_value_json as begin if a_other is of (ut_data_value_json) then l_other := treat(a_other as ut_data_value_json); - select case when json_equal(self.data_value, l_other.data_value) then 0 else 1 end - into l_result - from dual; + --select case when json_equal(self.data_value, l_other.data_value) then 0 else 1 end + --into l_result + --from dual; + l_result := case when self.json_tree.equals( l_other.json_tree, a_match_options ) then 0 else 1 end; end if; return l_result; end; + + overriding member function get_object_info return varchar2 is + begin + return self.data_type; + end; end; / diff --git a/source/expectations/data_values/ut_data_value_json.tps b/source/expectations/data_values/ut_data_value_json.tps index c14c88198..a89cf19cc 100644 --- a/source/expectations/data_values/ut_data_value_json.tps +++ b/source/expectations/data_values/ut_data_value_json.tps @@ -1,4 +1,4 @@ -create or replace type ut_data_value_json under ut_data_value( +create or replace type ut_data_value_json under ut_compound_data_value( /* utPLSQL - Version 3 Copyright 2016 - 2018 utPLSQL Project @@ -15,13 +15,15 @@ create or replace type ut_data_value_json under ut_data_value( See the License for the specific language governing permissions and limitations under the License. */ - is_data_null integer, - data_value clob, + data_value clob, + json_tree ut_json_tree_details, constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result, overriding member function is_null return boolean, + overriding member function is_empty return boolean, overriding member function to_string return varchar2, overriding member function diff( a_other ut_data_value, a_match_options ut_matcher_options ) return varchar2, overriding member function compare_implementation(a_other ut_data_value) return integer, - member function compare_implementation(a_other ut_data_value,a_match_options ut_matcher_options) return integer + member function compare_implementation(a_other ut_data_value,a_match_options ut_matcher_options) return integer, + overriding member function get_object_info return varchar2 ) / diff --git a/source/expectations/data_values/ut_json_leaf.tpb b/source/expectations/data_values/ut_json_leaf.tpb new file mode 100644 index 000000000..f6db72a16 --- /dev/null +++ b/source/expectations/data_values/ut_json_leaf.tpb @@ -0,0 +1,31 @@ +create or replace type body ut_json_leaf as + + member procedure init( self in out nocopy ut_json_leaf, + a_element_name varchar2, a_element_value varchar2,a_parent_name varchar2, + a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, + a_parent_type varchar2, a_array_element integer:=0) is + begin + self.element_name := a_element_name; + self.element_value := a_element_value; + self.parent_name := a_parent_name; + self.hierarchy_level := a_hierarchy_level; + self.access_path := a_access_path; + self.index_position := a_index_position; + self.json_type := a_json_type; + self.is_array_element := a_array_element; + self.parent_type := a_parent_type; + end; + + constructor function ut_json_leaf( self in out nocopy ut_json_leaf, + a_element_name varchar2, a_element_value varchar2,a_parent_name varchar2, + a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, + a_parent_type varchar2, a_array_element integer:=0) + return self as result is + begin + init(a_element_name,a_element_value,a_parent_name, a_access_path, a_hierarchy_level, a_index_position, + a_json_type,a_parent_type,a_array_element); + return; + end; + +end; +/ diff --git a/source/expectations/data_values/ut_json_leaf.tps b/source/expectations/data_values/ut_json_leaf.tps new file mode 100644 index 000000000..46b485d09 --- /dev/null +++ b/source/expectations/data_values/ut_json_leaf.tps @@ -0,0 +1,41 @@ +create or replace type ut_json_leaf force authid current_user as object ( + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + element_name varchar2(4000), + element_value varchar2(4000), + parent_name varchar2(4000), + access_path varchar2(4000), + tlength integer, + display_path varchar2(4000), + hierarchy_level integer, + index_position integer, + json_type varchar2(2000), + is_array_element integer, + parent_type varchar2(2000), + + member procedure init(self in out nocopy ut_json_leaf, + a_element_name varchar2, a_element_value varchar2,a_parent_name varchar2, + a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, + a_parent_type varchar2, a_array_element integer:=0), + + constructor function ut_json_leaf( self in out nocopy ut_json_leaf, + a_element_name varchar2, a_element_value varchar2,a_parent_name varchar2, + a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, + a_parent_type varchar2, a_array_element integer:=0) + return self as result +) +/ diff --git a/source/expectations/data_values/ut_json_leaf_tab.tps b/source/expectations/data_values/ut_json_leaf_tab.tps new file mode 100644 index 000000000..50a247ad6 --- /dev/null +++ b/source/expectations/data_values/ut_json_leaf_tab.tps @@ -0,0 +1,19 @@ +create or replace type ut_json_leaf_tab as + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +table of ut_json_leaf +/ \ No newline at end of file diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb new file mode 100644 index 000000000..6e295af36 --- /dev/null +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -0,0 +1,185 @@ +create or replace type body ut_json_tree_details as + + --TODO : Equal to be reviewed specially order of arrays and order of objects + member function equals( a_other ut_json_tree_details, a_match_options ut_matcher_options ) return boolean is + l_diffs integer; + begin + select count(1) into l_diffs + from table(self.json_tree_info) a + full outer join table(a_other.json_tree_info) e + on decode(a.parent_name,e.parent_name,1,0)= 1 + and decode(a.element_name,e.element_name,1,0) = 1 + and a.json_type = e.json_type + and a.hierarchy_level = e.hierarchy_level + and decode(a.element_value,e.element_value,1,0) = 1 + where a.element_name is null or e.element_name is null; + return l_diffs = 0; + end; + + member function get_json_type(a_json_piece json_element_t) return varchar2 is + begin + return case + when a_json_piece.is_object then 'object' + when a_json_piece.is_array then 'array' + when a_json_piece.is_string then 'string' + when a_json_piece.is_number then 'number' + when a_json_piece.is_boolean then 'boolean' + when a_json_piece.is_true then 'true' + when a_json_piece.is_false then 'false' + when a_json_piece.is_null then 'null' + when a_json_piece.is_date then 'date' + when a_json_piece.is_timestamp then 'timestamp' + when a_json_piece.is_scalar then 'scalar' + else null + end; + end; + + member function get_json_value(a_json_piece json_object_t,a_key varchar2) return varchar2 is + l_json_el json_element_t := a_json_piece.get (a_key); + l_val varchar2(4000); + begin + case + when l_json_el.is_string then l_val := a_json_piece.get_string(a_key); + when l_json_el.is_number then l_val := to_char(a_json_piece.get_number(a_key)); + when l_json_el.is_boolean then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); + when l_json_el.is_true then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); + when l_json_el.is_false then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); + when l_json_el.is_date then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR'); + when a_json_piece.is_timestamp then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); + else null; + end case; + return l_val; + end; + + member function get_json_value(a_json_piece json_array_t,a_key integer) return varchar2 is + l_json_el json_element_t := a_json_piece.get (a_key); + l_val varchar2(4000); + begin + case + when l_json_el.is_string then l_val := a_json_piece.get_string(a_key); + when l_json_el.is_number then l_val := to_char(a_json_piece.get_number(a_key)); + when l_json_el.is_boolean then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); + when l_json_el.is_true then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); + when l_json_el.is_false then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); + when l_json_el.is_date then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR'); + when a_json_piece.is_timestamp then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); + else null; + end case; + return l_val; + end; + + member function get_json_size(a_json_piece json_object_t) return integer is + begin + return a_json_piece.get_size; + end; + + member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, + a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, + a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0) is + begin + self.json_tree_info.extend; + self.json_tree_info(self.json_tree_info.last) := ut_json_leaf(a_element_name,a_element_value,a_parent_name,a_access_path, + a_hierarchy_level, a_index_position,a_json_type, a_parent_type, a_array_element); + end; + + member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_object_t, + a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as + l_array json_array_t; + l_object json_object_t; + l_keys json_key_list; + l_element json_element_t; + begin + l_keys := coalesce(a_json_piece.get_keys,json_key_list()); + + for indx in 1 .. l_keys.count + loop + add_json_leaf(l_keys(indx), + get_json_value(a_json_piece,l_keys(indx)), + a_parent_name, + a_access_path||'.'||l_keys(indx), + a_hierarchy_level, + indx, + get_json_type(a_json_piece.get (l_keys(indx))), + 'object' + ); + case get_json_type(a_json_piece.get(l_keys(indx))) + when 'array' then + traverse_array ( + treat (a_json_piece.get (l_keys(indx)) as json_array_t), + l_keys(indx), + a_hierarchy_level + 1, + a_access_path||'.'||l_keys(indx) + ); + when 'object' then + traverse_object( treat (a_json_piece.get (l_keys(indx)) as json_object_t), + l_keys (indx), + a_hierarchy_level+1, + a_access_path||'.'||l_keys(indx) + ); + else null; + end case; + end loop; + end traverse_object; + + member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_array_t, + a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as + l_array json_array_t; + l_object json_object_t; + l_element json_element_t; + begin + l_array := a_json_piece; + + for indx in 0 .. l_array.get_size - 1 + loop + add_json_leaf(l_array.get(indx).stringify, + get_json_value(a_json_piece,indx), + a_parent_name, + a_access_path||'['||indx||']', + a_hierarchy_level, + indx, + get_json_type(l_array.get (indx)), + 'array', + 1 + ); + case get_json_type(l_array.get (indx)) + when 'array' then + traverse_array ( + treat (l_array.get (indx) as json_array_t), + l_array.get(indx).stringify, + a_hierarchy_level + 1, + a_access_path||'['||indx||']' + ); + when 'object' then + traverse_object( + treat (a_json_piece.get (indx) as json_object_t), + l_array.get(indx).stringify, + a_hierarchy_level+1, + a_access_path||'['||indx||']' + ); + else null; + end case; + end loop; + end traverse_array; + + member procedure init(self in out nocopy ut_json_tree_details,a_json_doc in json_element_t, a_level_in integer := 0) is + begin + if a_json_doc.is_object then + traverse_object(treat (a_json_doc as json_object_t),null,1,'$'); + elsif a_json_doc.is_array then + traverse_array(treat (a_json_doc as json_array_t),null,1,'$'); + end if; + end; + + constructor function ut_json_tree_details( + self in out nocopy ut_json_tree_details, a_json_doc in json_element_t, a_level_in integer := 0 + ) return self as result is + begin + self.json_tree_info := ut_json_leaf_tab(); + if a_json_doc is not null then + init(a_json_doc,a_level_in); + end if; + return; + end; + +end; +/ diff --git a/source/expectations/data_values/ut_json_tree_details.tps b/source/expectations/data_values/ut_json_tree_details.tps new file mode 100644 index 000000000..3da95f291 --- /dev/null +++ b/source/expectations/data_values/ut_json_tree_details.tps @@ -0,0 +1,36 @@ +create or replace type ut_json_tree_details force as object ( + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + json_tree_info ut_json_leaf_tab, + member function equals(a_other ut_json_tree_details, a_match_options ut_matcher_options) return boolean, + member function get_json_type(a_json_piece json_element_t) return varchar2, + member function get_json_value(a_json_piece json_object_t,a_key varchar2) return varchar2, + member function get_json_value(a_json_piece json_array_t,a_key integer) return varchar2, + member function get_json_size(a_json_piece json_object_t) return integer, + member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, + a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, + a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0), + member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_object_t, + a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ), + member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_array_t, + a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ), + member procedure init(self in out nocopy ut_json_tree_details,a_json_doc in json_element_t, a_level_in integer := 0), + constructor function ut_json_tree_details( + self in out nocopy ut_json_tree_details,a_json_doc in json_element_t, a_level_in integer := 0 + ) return self as result +) +/ diff --git a/source/expectations/matchers/ut_equal.tpb b/source/expectations/matchers/ut_equal.tpb index 6fb85532f..2a8af6c91 100644 --- a/source/expectations/matchers/ut_equal.tpb +++ b/source/expectations/matchers/ut_equal.tpb @@ -248,6 +248,11 @@ create or replace type body ut_equal as 'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info() || chr(10) || 'Diff:' || treat(expected as ut_data_value_refcursor).diff( a_actual, options ); + elsif self.expected is of (ut_data_value_json) then + l_result := + 'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info() + || chr(10) || 'Diff:' || + treat(expected as ut_data_value_json).diff( a_actual, options ); else l_result := 'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info() diff --git a/source/expectations/ut_expectation_json.tpb b/source/expectations/ut_expectation_json.tpb index cda83f691..62a459ffc 100644 --- a/source/expectations/ut_expectation_json.tpb +++ b/source/expectations/ut_expectation_json.tpb @@ -33,16 +33,6 @@ create or replace type body ut_expectation_json as self.not_to( ut_be_empty() ); end; - member procedure to_have_count(self in ut_expectation_json, a_expected integer) is - begin - self.to_( ut_have_count(a_expected) ); - end; - - member procedure not_to_have_count(self in ut_expectation_json, a_expected integer) is - begin - self.not_to( ut_have_count(a_expected) ); - end; - member function to_equal(a_expected json_element_t, a_nulls_are_equal boolean := null) return ut_expectation_json is l_result ut_expectation_json := self; begin diff --git a/source/expectations/ut_expectation_json.tps b/source/expectations/ut_expectation_json.tps index b79268bd0..4e840f2c4 100644 --- a/source/expectations/ut_expectation_json.tps +++ b/source/expectations/ut_expectation_json.tps @@ -21,14 +21,6 @@ create or replace type ut_expectation_json force under ut_expectation( member procedure to_be_empty(self in ut_expectation_json), member procedure not_to_be_empty(self in ut_expectation_json), - member procedure to_have_count(self in ut_expectation_json, a_expected integer), - member procedure not_to_have_count(self in ut_expectation_json, a_expected integer), - /* - member procedure to_contain_text(self in ut_expectation_json, a_expected clob), - member procedure not_to_contain_text(self in ut_expectation_json, a_expected clob), - member procedure to_exists(self in ut_expectation_json, a_expected clob), - member procedure not_to_exists(self in ut_expectation_json, a_expected clob), - */ member function to_equal(a_expected json_element_t , a_nulls_are_equal boolean := null) return ut_expectation_json, member function not_to_equal(a_expected json_element_t , a_nulls_are_equal boolean := null) return ut_expectation_json ) diff --git a/source/install.sql b/source/install.sql index 863d4c7c6..9d4c94ef9 100644 --- a/source/install.sql +++ b/source/install.sql @@ -193,6 +193,9 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'expectations/data_values/ut_compound_data_tmp.sql' @@install_component.sql 'expectations/data_values/ut_compound_data_diff_tmp.sql' @@install_component.sql 'expectations/data_values/ut_compound_data_value.tps' +@@install_component.sql 'expectations/data_values/ut_json_leaf.tps' +@@install_component.sql 'expectations/data_values/ut_json_leaf_tab.tps' +@@install_component.sql 'expectations/data_values/ut_json_tree_details.tps' @@install_component.sql 'expectations/data_values/ut_cursor_column.tps' @@install_component.sql 'expectations/data_values/ut_cursor_column_tab.tps' @@install_component.sql 'expectations/data_values/ut_cursor_details.tps' @@ -230,6 +233,8 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'expectations/matchers/ut_be_empty.tps' @@install_component.sql 'expectations/matchers/ut_match.tps' @@install_component.sql 'expectations/ut_expectation.tps' +@@install_component.sql 'expectations/data_values/ut_json_leaf.tpb' +@@install_component.sql 'expectations/data_values/ut_json_tree_details.tpb' @@install_component.sql 'expectations/data_values/ut_cursor_column.tpb' @@install_component.sql 'expectations/data_values/ut_cursor_details.tpb' @@install_component.sql 'expectations/ut_expectation_compound.tps' diff --git a/source/uninstall_objects.sql b/source/uninstall_objects.sql index e2295f34d..8a124e001 100644 --- a/source/uninstall_objects.sql +++ b/source/uninstall_objects.sql @@ -147,6 +147,12 @@ drop type ut_matcher_options force; drop type ut_matcher_options_items force; +drop type ut_json_tree_details force; + +drop type ut_json_leaf_tab force; + +drop type ut_json_leaf; + drop type ut_cursor_details force; drop type ut_cursor_column_tab force; diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index 1f2b06048..10ae1c960 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -34,7 +34,11 @@ create or replace package body test_expectations_json is --Act ut3.ut.expect( l_actual ).to_equal( l_expected ); --Assert - l_expected_message := q'[%Actual: '{"name1":"value1","name2":"value2"}' (json) was expected to equal: '{"name1":"value2","name2":"value2"}' (json)%]'; + l_expected_message := q'[%Actual: json was expected to equal: json +%Diff: +%Found: 1 difference. +%1 unequal values +%Actual value is value2 was expected to be value1%]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert ut.expect(l_actual_message).to_be_like(l_expected_message); @@ -53,16 +57,15 @@ create or replace package body test_expectations_json is ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; - procedure null_json + procedure empty_json as - l_expected json_element_t; - l_actual json_element_t; + l_expected JSON_OBJECT_T; begin -- Arrange l_expected := JSON_OBJECT_T(); --Act - ut3.ut.expect( l_expected ).to_be_null; + ut3.ut.expect( l_expected ).to_be_empty; --Assert ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index 641dbf179..4a65a2280 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -16,8 +16,8 @@ create or replace package test_expectations_json is --%test( Json variable is null) procedure null_json_variable; - --%test(Json string is null) - procedure null_json; + --%test(Json string is empty) + procedure empty_json; end; / From ba1392a0b966e8171c873719547bf3972edd5d17 Mon Sep 17 00:00:00 2001 From: lwasylow Date: Tue, 21 May 2019 12:06:50 +0100 Subject: [PATCH 03/24] Update tests. Update sonar rules. --- .../data_values/ut_compound_data_helper.pkb | 16 +++- .../data_values/ut_compound_data_helper.pks | 4 +- .../data_values/ut_data_value_json.tpb | 40 ++++---- .../data_values/ut_json_tree_details.tpb | 9 +- .../expectations/test_expectations_json.pkb | 91 +++++++++++++++++-- .../expectations/test_expectations_json.pks | 12 +++ 6 files changed, 134 insertions(+), 38 deletions(-) diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index c8c819978..42784693a 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -637,12 +637,16 @@ create or replace package body ut_compound_data_helper is a.element_name as act_element_name, a.element_value as act_element_value, a.hierarchy_level as act_hierarchy_level, + a.index_position as act_index_position, a.json_type as act_json_type, + a.access_path as act_access_path, + a.parent_name act_par_name, e.element_name as exp_element_name, e.element_value as exp_element_value, e.hierarchy_level as exp_hierarchy_level, + e.index_position as exp_index_position, e.json_type as exp_json_type, - a.parent_name act_par_name, + e.access_path as exp_access_path, e.parent_name exp_par_name from table(a_act_json_data) a full outer join table(a_exp_json_data) e @@ -661,20 +665,22 @@ create or replace package body ut_compound_data_helper is where (a.element_name is null or e.element_name is null) or (a.json_type != e.json_type) or (decode(a.element_value,e.element_value,1,0) = 0) - order by nvl(a.hierarchy_level,e.hierarchy_level),nvl(a.index_position,e.index_position) ) select difference_type, act_element_name, act_element_value, act_json_type, + act_access_path, exp_element_name, exp_element_value, - exp_json_type + exp_json_type, + exp_access_path bulk collect into l_result_diff from differences a where not exists ( select 1 from differences b where (a.act_par_name = b.act_element_name and a.act_hierarchy_level - 1 = b.act_hierarchy_level) - or (a.exp_par_name = b.exp_element_name and a.exp_hierarchy_level - 1 = b.exp_hierarchy_level)); - + or (a.exp_par_name = b.exp_element_name and a.exp_hierarchy_level - 1 = b.exp_hierarchy_level)) + order by nvl(act_hierarchy_level,exp_hierarchy_level),nvl(act_index_position,exp_index_position) nulls first, + nvl(act_element_name,exp_element_name),nvl(act_json_type,exp_json_type) ; return l_result_diff; end; diff --git a/source/expectations/data_values/ut_compound_data_helper.pks b/source/expectations/data_values/ut_compound_data_helper.pks index 151bf063f..adb91d296 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pks +++ b/source/expectations/data_values/ut_compound_data_helper.pks @@ -61,9 +61,11 @@ create or replace package ut_compound_data_helper authid definer is act_element_name varchar2(4000), act_element_value varchar2(4000), act_json_type varchar2(4000), + act_access_path varchar2(4000), exp_element_name varchar2(4000), exp_element_value varchar2(4000), - exp_json_type varchar2(4000) + exp_json_type varchar2(4000), + exp_access_path varchar2(4000) ); type tt_json_diff_tab is table of t_json_diff_rec; diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb index 5c093efdc..57b6e46ba 100644 --- a/source/expectations/data_values/ut_data_value_json.tpb +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -51,13 +51,11 @@ create or replace type body ut_data_value_json as l_self ut_data_value_json := self; c_max_rows integer := ut_utils.gc_diff_max_rows; - l_diff_row_count integer; l_diffs ut_compound_data_helper.tt_json_diff_tab; l_message varchar2(32767); function get_diff_by_type(a_diff ut_compound_data_helper.tt_json_diff_tab) return clob is - l_diff_summary ut_compound_data_helper.tt_json_diff_type_tab := ut_compound_data_helper.get_json_diffs_type(a_diff); - l_message varchar2(32767); + l_diff_summary ut_compound_data_helper.tt_json_diff_type_tab := ut_compound_data_helper.get_json_diffs_type(a_diff); l_message_list ut_varchar2_list := ut_varchar2_list(); begin for i in 1..l_diff_summary.count loop @@ -69,16 +67,21 @@ create or replace type body ut_data_value_json as function get_json_diff_text (a_json_diff ut_compound_data_helper.t_json_diff_rec) return clob is begin - return case - when a_json_diff.difference_type = ut_compound_data_helper.gc_json_missing and a_json_diff.act_element_name is not null - then 'Missing property '||a_json_diff.act_element_name - when a_json_diff.difference_type = ut_compound_data_helper.gc_json_missing and a_json_diff.exp_element_name is not null - then 'Extra property '||a_json_diff.exp_element_name - when a_json_diff.difference_type = ut_compound_data_helper.gc_json_type - then 'Actual type is '||a_json_diff.act_json_type||' was expected to be '||a_json_diff.exp_json_type - when a_json_diff.difference_type = ut_compound_data_helper.gc_json_notequal - then 'Actual value is '||a_json_diff.act_element_value||' was expected to be '||a_json_diff.exp_element_value - else 'Unknown' end; + return + case a_json_diff.difference_type + when ut_compound_data_helper.gc_json_missing + then + case + when a_json_diff.act_element_name is not null then 'Missing property "'||a_json_diff.act_element_name||'"' + when a_json_diff.exp_element_name is not null then 'Extra property "'||a_json_diff.exp_element_name||'"' + else 'Unknown' + end + when ut_compound_data_helper.gc_json_type + then 'Actual type is "'||a_json_diff.act_json_type||'" was expected to be "'||a_json_diff.exp_json_type||'"' + when ut_compound_data_helper.gc_json_notequal + then 'Actual value is "'||a_json_diff.act_element_value||'" was expected to be "'||a_json_diff.exp_element_value||'"' + else 'Unknown' + end || ' on path :'||nvl(a_json_diff.act_access_path,a_json_diff.exp_access_path); end; begin @@ -89,15 +92,16 @@ create or replace type body ut_data_value_json as if not l_self.is_null and not l_other.is_null then l_diffs := ut_compound_data_helper.get_json_diffs( - l_self.json_tree.json_tree_info, - l_other.json_tree.json_tree_info); + l_other.json_tree.json_tree_info, + l_self.json_tree.json_tree_info); - l_message:= chr(10)||'Found: '||l_diffs.count|| case when l_diffs.count > 1 then ' differences.' else ' difference.' end||chr(10); + l_message:= chr(10)||'Found: '||l_diffs.count|| ' differences' || + case when l_diffs.count > c_max_rows then ', showing first '||c_max_rows else null end||chr(10); ut_utils.append_to_clob( l_result, l_message ); l_message:= get_diff_by_type(l_diffs)||chr(10); ut_utils.append_to_clob( l_result, l_message ); - for i in 1..l_diffs.count loop + for i in 1..least(c_max_rows,l_diffs.count) loop l_results.extend; l_results(l_results.last) := get_json_diff_text(l_diffs(i)); end loop; @@ -107,7 +111,7 @@ create or replace type body ut_data_value_json as l_result_string := ut_utils.to_string(l_result,null); - --dbms_lob.freetemporary(l_result); + dbms_lob.freetemporary(l_result); return l_result_string; end; diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index 6e295af36..98d7077d4 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -84,10 +84,7 @@ create or replace type body ut_json_tree_details as member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_object_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as - l_array json_array_t; - l_object json_object_t; - l_keys json_key_list; - l_element json_element_t; + l_keys json_key_list; begin l_keys := coalesce(a_json_piece.get_keys,json_key_list()); @@ -123,9 +120,7 @@ create or replace type body ut_json_tree_details as member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_array_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as - l_array json_array_t; - l_object json_object_t; - l_element json_element_t; + l_array json_array_t; begin l_array := a_json_piece; diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index 10ae1c960..550f9adcd 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -28,17 +28,30 @@ create or replace package body test_expectations_json is l_actual_message varchar2(32767); begin -- Arrange - l_expected := json_element_t.parse('{"name1":"value2","name2":"value2"}'); - l_actual := json_element_t.parse('{"name1":"value1","name2":"value2"}'); + l_actual := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + l_expected := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thron\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true,"object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"], "Clarke Peters": null}'); --Act ut3.ut.expect( l_actual ).to_equal( l_expected ); --Assert - l_expected_message := q'[%Actual: json was expected to equal: json -%Diff: -%Found: 1 difference. -%1 unequal values -%Actual value is value2 was expected to be value1%]'; + l_expected_message := q'[%%Actual type is "array" was expected to be "object" on path :$.Amy Ryan +%Missing property "Alexander Skarsg?rd" on path :$.Alexander Skarsg?rd +%Extra property "Alexander Skarsgard" on path :$.Alexander Skarsgard +%Missing property "Alice Farmer" on path :$.Alice Farmer +%Extra property "Clarke Peters" on path :$.Clarke Peters +%Actual value is "True Blood" was expected to be "Big Love" on path :$.Annie Fitzgerald[0] +%Actual value is "Big Love" was expected to be "True Blood" on path :$.Annie Fitzgerald[1] +%Missing property ""The Sopranos"" on path :$.Annie Fitzgerald[2] +%Missing property ""Oz"" on path :$.Annie Fitzgerald[3] +%Actual type is "string" was expected to be "number" on path :$.Aidan Gillen.int +%Missing property "otherint" on path :$.Aidan Gillen.otherint +%Actual type is "string" was expected to be "boolean" on path :$.Aidan Gillen.aboolean +%Actual value is "0" was expected to be "1" on path :$.Aidan Gillen.boolean +%Actual value is "Game of Thrones" was expected to be "Game of Thron"es" on path :$.Aidan Gillen.array[0] +%Extra property "object1" on path :$.Aidan Gillen.object.object1 +%Extra property "object2" on path :$.Aidan Gillen.object.object2 +%Extra property "object3" on path :$.Aidan Gillen.object.object3 +%Extra property "object4" on path :$.Aidan Gillen.object.object4]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert ut.expect(l_actual_message).to_be_like(l_expected_message); @@ -57,6 +70,57 @@ create or replace package body test_expectations_json is ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; + procedure not_null_json_variable + as + l_expected json_object_t ; + begin + -- Arrange + l_expected := JSON_OBJECT_T(); + + --Act + ut3.ut.expect( l_expected ).not_to_be_null; + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure fail_null_json_var + as + l_expected json_object_t ; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_expected := JSON_OBJECT_T('{ "t" : "1" }'); + + --Act + ut3.ut.expect( l_expected ).to_be_null; + --Assert + l_expected_message := q'[%Actual: (json) +%'{"t":"1"}' +%was expected to be null%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure fail_not_null_json_var + as + l_expected json_object_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_expected := cast (null as JSON_OBJECT_T ); + + --Act + ut3.ut.expect( l_expected ).not_to_be_null; + --Assert + l_expected_message := q'[%Actual: NULL (json) was expected not to be null%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + procedure empty_json as l_expected JSON_OBJECT_T; @@ -70,5 +134,18 @@ create or replace package body test_expectations_json is ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; + procedure not_empty_json + as + l_expected JSON_OBJECT_T; + begin + -- Arrange + l_expected := JSON_OBJECT_T.parse('{ "name" : "test" }'); + + --Act + ut3.ut.expect( l_expected ).not_to_be_empty; + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + end; / diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index 4a65a2280..9db9fd5c7 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -16,8 +16,20 @@ create or replace package test_expectations_json is --%test( Json variable is null) procedure null_json_variable; + --%test( Json variable is not null) + procedure not_null_json_variable; + + --%test( Fail json variable is null) + procedure fail_null_json_var; + + --%test( Fail json variable is not null) + procedure fail_not_null_json_var; + --%test(Json string is empty) procedure empty_json; + + --%test(Json string is not empty) + procedure not_empty_json; end; / From 021e48d34067e8076fdd32129acf77e7ff723b31 Mon Sep 17 00:00:00 2001 From: lwasylow Date: Tue, 21 May 2019 14:42:35 +0100 Subject: [PATCH 04/24] small fixes --- docs/userguide/advanced_data_comparison.md | 125 ++++++++++++++++-- docs/userguide/expectations.md | 23 ++++ source/core/ut_utils.pkb | 5 + source/core/ut_utils.pks | 2 + .../data_values/ut_compound_data_helper.pkb | 29 +--- .../data_values/ut_json_tree_details.tpb | 12 +- .../expectations/test_expectations_json.pkb | 58 ++++++-- .../expectations/test_expectations_json.pks | 6 + 8 files changed, 213 insertions(+), 47 deletions(-) diff --git a/docs/userguide/advanced_data_comparison.md b/docs/userguide/advanced_data_comparison.md index f2f296755..87d3f5722 100644 --- a/docs/userguide/advanced_data_comparison.md +++ b/docs/userguide/advanced_data_comparison.md @@ -7,10 +7,15 @@ utPLSQL expectations incorporates advanced data comparison options when comparin - refcursor - object type - nested table and varray +- json data-types Advanced data-comparison options are available for the [`equal`](expectations.md#equal) and [`contain`](expectations.md#include--contain) matcher. -## Syntax +In addition json data-types supports advanced matcher `to_have_count` + +## Refcursor, object type, nested table and varray + +### Syntax ``` ut.expect( a_actual {data-type} ).to_( equal( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); @@ -44,7 +49,7 @@ Each element in `ut_varchar2_list` nested table can be an item or a comma separa When specifying column/attribute names, keep in mind that the names are **case sensitive**. -## Excluding elements from data comparison +### Excluding elements from data comparison Consider the following examples ```sql @@ -72,7 +77,7 @@ The cursor data is equal or includes expected, when those columns are excluded. This option is useful in scenarios, when you need to exclude incomparable/unpredictable column data like CREATE_DATE of a record that is maintained by default value on a table column. -## Selecting columns for data comparison +### Selecting columns for data comparison Consider the following example ```sql @@ -95,7 +100,7 @@ begin end; ``` -## Combining include/exclude options +### Combining include/exclude options You can chain the advanced options in an expectation and mix the `varchar2` with `ut_varchar2_list` arguments. When doing so, the final list of items to include/exclude will be a concatenation of all items. @@ -130,7 +135,7 @@ Only the columns 'RN', "A_Column" will be compared. Column 'SOME_COL' is exclude This option can be useful in scenarios where you need to narrow-down the scope of test so that the test is only focused on very specific data. -## Unordered +### Unordered Unordered option allows for quick comparison of two compound data types without need of ordering them in any way. @@ -169,7 +174,7 @@ Above test will result in two differences of one row extra and one row missing. > `contain` matcher is not considering order of compared data-sets. Using `unordered` makes no difference (it's default) -## Join By option +### Join By option The `join_by` syntax enables comparison of unordered compound data types by joining data using specified columns. @@ -239,7 +244,7 @@ Above test will indicate that in actual data-set ``` -### Joining using multiple columns +#### Joining using multiple columns You can specify multiple columns in `join_by` @@ -260,7 +265,7 @@ begin end; ``` -### Joining using attributes of object in column list +#### Joining using attributes of object in column list `join_by` allows for joining data by attributes of object from column list of the compared compound data types. @@ -354,7 +359,7 @@ Unable to join sets: ***Note*** >`join_by` option is slower to process as it needs to perform a cursor join. -## Defining item lists in option +### Defining item lists in option XPath expressions are deprecated. They are currently still supported but in future versions they can be removed completely. Please use a current standard of defining items filter. When using item list expression, keep in mind the following: @@ -375,7 +380,7 @@ begin end; ``` -## Unordered columns / uc option +### Unordered columns / uc option If you need to perform data comparison of compound data types without strictly depending on column order in the returned result-set, use the `unordered_columns` option. Shortcut name `uc` is also available for that option. @@ -424,3 +429,103 @@ Finished in .046193 seconds 1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` + + +## Json Data-types + +### Syntax + +``` + ut.expect( a_actual {data-type} ).to_( equal( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); + ut.expect( a_actual {data-type} ).not_to( equal( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]) ); + ut.expect( a_actual {data-type} ).to_equal( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); + ut.expect( a_actual {data-type} ).not_to_equal( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]] ); + ut.expect( a_actual {data-type} ).to_( contain( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); + ut.expect( a_actual {data-type} ).not_to( contain( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]) ); + ut.expect( a_actual {data-type} ).to_contain( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); + ut.expect( a_actual {data-type} ).not_to_contain( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); +``` + +`extended_option` can be one of: + +- `include(a_items varchar2)` - item or comma separated list of items to include + +- `exclude(a_items varchar2)` - item or comma separated list of items to exclude + +- `include(a_items ut_varchar2_list)` - table of items to include + +- `exclude(a_items ut_varchar2_list)` - table of items to exclude + +- `to_have_count(a_items ut_varchar2)` - Items to be included in count + +- `to_have_count(a_items ut_varchar2_list)` - table of items to be included in count + + +Each item in the comma separated list can be: + +- a column name of cursor to be compared +- an attribute name of object type to be compared +- an attribute name of object type within a table of objects to be compared +- Include and exclude option will not support implicit colum names that starts with single quota, or in fact any other special characters e.g. <, >, & + +Each element in `ut_varchar2_list` nested table can be an item or a comma separated list of items. + +When specifying column/attribute names, keep in mind that the names are **case sensitive**. + +### Excluding elements from data comparison + +Consider the following examples + +```sql +procedure test_cur_skip_columns_eq is + l_expected sys_refcursor; + l_actual sys_refcursor; +begin + open l_expected for select 'text' ignore_me, d.* from user_tables d; + open l_actual for select sysdate "ADate", d.* from user_tables d; + ut.expect( l_actual ).to_equal( l_expected ).exclude( 'IGNORE_ME,ADate' ); +end; + +procedure test_cur_skip_columns_cn is + l_expected sys_refcursor; + l_actual sys_refcursor; +begin + open l_expected for select 'text' ignore_me, d.* from user_tables d where rownum = 1; + open l_actual for select sysdate "ADate", d.* from user_tables d; + ut.expect( l_actual ).to_contain( l_expected ).exclude( 'IGNORE_ME,ADate' ); +end; +``` + +Columns 'ignore_me' and "ADate" will get excluded from cursor comparison. +The cursor data is equal or includes expected, when those columns are excluded. + +This option is useful in scenarios, when you need to exclude incomparable/unpredictable column data like CREATE_DATE of a record that is maintained by default value on a table column. + +### Selecting columns for data comparison + +Consider the following example + +```sql +procedure include_col_as_csv_eq is + l_actual sys_refcursor; + l_expected sys_refcursor; +begin + open l_expected for select rownum as rn, 'a' as "A_Column", 'x' SOME_COL from dual a connect by level < 4; + open l_actual for select rownum as rn, 'a' as "A_Column", 'x' SOME_COL, a.* from all_objects a where rownum < 4; + ut.expect( l_actual ).to_equal( l_expected ).include( 'RN,A_Column,SOME_COL' ); +end; + +procedure include_col_as_csv_cn is + l_actual sys_refcursor; + l_expected sys_refcursor; +begin + open l_expected for select rownum as rn, 'a' as "A_Column", 'x' SOME_COL from dual a connect by level < 4; + open l_actual for select rownum as rn, 'a' as "A_Column", 'x' SOME_COL, a.* from all_objects a where rownum < 6; + ut.expect( l_actual ).to_contain( l_expected ).include( 'RN,A_Column,SOME_COL' ); +end; +``` + +### Counting elements of json + +`to_have_count` supports json objects,scalars and arrays. For json objects it will return a number of keys, for arrays number of elements and for scalar always 1. As an argument into matcher you can pass a json path to element that will be count. If this is null it will take a current object. + diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index 51dbb4e69..9a3dc2236 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -1177,7 +1177,30 @@ Finished in .048181 seconds ``` + +# Comparing json objects + +utPLSQL is capable of comparing json data-types on Oracle 12.2 and above. + +### Notes on comparison of json data + +- Json data can contain objects, scalar or arrays. +- During comparison of json objects the order doesn't matter. +- During comparison of json arrays the index of element is taken into account +- To compare json you have to make sure its type of `json_element_t` or its subtypes + +utPLSQL offers advanced data-comparison options, for comparing json data-types. The options allow you to: + +- define json path query to exclude from comparison +- define json path query to include in comparison +- and more ... + +For details on available options and how to use them, read the [advanced data comparison](advanced_data_comparison.md) guide. + + + # Negating a matcher + Expectations provide a very convenient way to perform a check on a negated matcher. Syntax to check for matcher evaluating to true: diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 92ee0895d..b6275a9e8 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -199,6 +199,11 @@ create or replace package body ut_utils is return case a_value when 1 then true when 0 then false end; end; + function boolean_to_char(a_value boolean) return varchar2 is + begin + return case a_value when true then 'true' when false then 'false' end; + end; + function string_to_table(a_string varchar2, a_delimiter varchar2:= chr(10), a_skip_leading_delimiter varchar2 := 'N') return ut_varchar2_list is l_offset integer := 1; l_delimiter_position integer; diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 25487d9c3..5d92ad1b2 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -200,6 +200,8 @@ create or replace package ut_utils authid definer is function int_to_boolean(a_value integer) return boolean; + function boolean_to_char(a_value boolean) return varchar2; + /** * * Splits a given string into table of string by delimiter. diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index 42784693a..5a1559a1a 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -634,19 +634,11 @@ create or replace package body ut_compound_data_helper is when a.json_type != e.json_type then gc_json_type when (decode(a.element_value,e.element_value,1,0) = 0) then gc_json_notequal else gc_json_unknown end as difference_type, - a.element_name as act_element_name, - a.element_value as act_element_value, - a.hierarchy_level as act_hierarchy_level, - a.index_position as act_index_position, - a.json_type as act_json_type, - a.access_path as act_access_path, - a.parent_name act_par_name, - e.element_name as exp_element_name, - e.element_value as exp_element_value, - e.hierarchy_level as exp_hierarchy_level, - e.index_position as exp_index_position, - e.json_type as exp_json_type, - e.access_path as exp_access_path, + a.element_name as act_element_name, a.element_value as act_element_value, a.hierarchy_level as act_hierarchy_level, + a.index_position as act_index_position, a.json_type as act_json_type, a.access_path as act_access_path, + a.parent_name act_par_name, + e.element_name as exp_element_name, e.element_value as exp_element_value, e.hierarchy_level as exp_hierarchy_level, + e.index_position as exp_index_position, e.json_type as exp_json_type, e.access_path as exp_access_path, e.parent_name exp_par_name from table(a_act_json_data) a full outer join table(a_exp_json_data) e @@ -666,15 +658,8 @@ create or replace package body ut_compound_data_helper is or (a.json_type != e.json_type) or (decode(a.element_value,e.element_value,1,0) = 0) ) - select difference_type, - act_element_name, - act_element_value, - act_json_type, - act_access_path, - exp_element_name, - exp_element_value, - exp_json_type, - exp_access_path + select difference_type, act_element_name, act_element_value, act_json_type, act_access_path, + exp_element_name, exp_element_value, exp_json_type, exp_access_path bulk collect into l_result_diff from differences a where not exists ( select 1 from differences b where (a.act_par_name = b.act_element_name and a.act_hierarchy_level - 1 = b.act_hierarchy_level) diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index 98d7077d4..a3610a439 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -41,9 +41,9 @@ create or replace type body ut_json_tree_details as case when l_json_el.is_string then l_val := a_json_piece.get_string(a_key); when l_json_el.is_number then l_val := to_char(a_json_piece.get_number(a_key)); - when l_json_el.is_boolean then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); - when l_json_el.is_true then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); - when l_json_el.is_false then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); + when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); + when l_json_el.is_true then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); + when l_json_el.is_false then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); when l_json_el.is_date then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR'); when a_json_piece.is_timestamp then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); else null; @@ -58,9 +58,9 @@ create or replace type body ut_json_tree_details as case when l_json_el.is_string then l_val := a_json_piece.get_string(a_key); when l_json_el.is_number then l_val := to_char(a_json_piece.get_number(a_key)); - when l_json_el.is_boolean then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); - when l_json_el.is_true then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); - when l_json_el.is_false then l_val := ut_utils.boolean_to_int(a_json_piece.get_boolean(a_key)); + when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); + when l_json_el.is_true then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); + when l_json_el.is_false then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); when l_json_el.is_date then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR'); when a_json_piece.is_timestamp then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); else null; diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index 550f9adcd..c3b01d8e2 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -46,7 +46,7 @@ create or replace package body test_expectations_json is %Actual type is "string" was expected to be "number" on path :$.Aidan Gillen.int %Missing property "otherint" on path :$.Aidan Gillen.otherint %Actual type is "string" was expected to be "boolean" on path :$.Aidan Gillen.aboolean -%Actual value is "0" was expected to be "1" on path :$.Aidan Gillen.boolean +%Actual value is "false" was expected to be "true" on path :$.Aidan Gillen.boolean %Actual value is "Game of Thrones" was expected to be "Game of Thron"es" on path :$.Aidan Gillen.array[0] %Extra property "object1" on path :$.Aidan Gillen.object.object1 %Extra property "object2" on path :$.Aidan Gillen.object.object2 @@ -62,7 +62,7 @@ create or replace package body test_expectations_json is l_expected json_object_t ; begin -- Arrange - l_expected := cast (null as JSON_OBJECT_T ); + l_expected := cast (null as json_object_t ); --Act ut3.ut.expect( l_expected ).to_be_null; @@ -75,7 +75,7 @@ create or replace package body test_expectations_json is l_expected json_object_t ; begin -- Arrange - l_expected := JSON_OBJECT_T(); + l_expected := json_object_t(); --Act ut3.ut.expect( l_expected ).not_to_be_null; @@ -90,7 +90,7 @@ create or replace package body test_expectations_json is l_actual_message varchar2(32767); begin -- Arrange - l_expected := JSON_OBJECT_T('{ "t" : "1" }'); + l_expected := json_object_t('{ "t" : "1" }'); --Act ut3.ut.expect( l_expected ).to_be_null; @@ -110,7 +110,7 @@ create or replace package body test_expectations_json is l_actual_message varchar2(32767); begin -- Arrange - l_expected := cast (null as JSON_OBJECT_T ); + l_expected := cast (null as json_object_t ); --Act ut3.ut.expect( l_expected ).not_to_be_null; @@ -123,10 +123,10 @@ create or replace package body test_expectations_json is procedure empty_json as - l_expected JSON_OBJECT_T; + l_expected json_object_t; begin -- Arrange - l_expected := JSON_OBJECT_T(); + l_expected := json_object_t(); --Act ut3.ut.expect( l_expected ).to_be_empty; @@ -136,16 +136,56 @@ create or replace package body test_expectations_json is procedure not_empty_json as - l_expected JSON_OBJECT_T; + l_expected json_object_t; begin -- Arrange - l_expected := JSON_OBJECT_T.parse('{ "name" : "test" }'); + l_expected := json_object_t.parse('{ "name" : "test" }'); --Act ut3.ut.expect( l_expected ).not_to_be_empty; --Assert ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; + + procedure fail_empty_json + as + l_expected json_object_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_expected := json_object_t.parse('{ "name" : "test" }'); + + --Act + ut3.ut.expect( l_expected ).to_be_empty; + --Assert + l_expected_message := q'[%Actual: (json) +%'{"name":"test"}' +%was expected to be empty%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + procedure fail_not_empty_json + as + l_expected json_object_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_expected := json_object_t(); + + --Act + ut3.ut.expect( l_expected ).not_to_be_empty; + --Assert + l_expected_message := q'[%Actual: (json) +%'{}' +%was expected not to be empty%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + end; / diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index 9db9fd5c7..55e29f9e6 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -31,5 +31,11 @@ create or replace package test_expectations_json is --%test(Json string is not empty) procedure not_empty_json; + --%test( Fail json string is empty) + procedure fail_empty_json; + + --%test( Fail json string is not empty) + procedure fail_not_empty_json; + end; / From 86aa49d76e19d4c66c4bfb9084a55ee02a14d2da Mon Sep 17 00:00:00 2001 From: lwasylow Date: Wed, 22 May 2019 07:27:27 +0100 Subject: [PATCH 05/24] PHASE 2: Moving diff into temp table and init data compare during compare implementation to avoid finding same differences twice Adding have count and negated have count matcher.l --- docs/userguide/advanced_data_comparison.md | 98 -------------- docs/userguide/expectations.md | 10 -- .../data_values/ut_compound_data_helper.pkb | 29 ++++- .../data_values/ut_compound_data_helper.pks | 7 +- .../data_values/ut_data_value_json.tpb | 59 +++++---- .../data_values/ut_data_value_json.tps | 2 + .../data_values/ut_json_data_diff_tmp.sql | 25 ++++ .../data_values/ut_json_tree_details.tpb | 24 +--- .../data_values/ut_json_tree_details.tps | 3 +- .../expectations/matchers/ut_have_count.tpb | 10 +- source/expectations/ut_expectation_json.tpb | 10 ++ source/expectations/ut_expectation_json.tps | 6 +- source/install.sql | 1 + source/uninstall_objects.sql | 2 + .../expectations/test_expectations_json.pkb | 123 +++++++++++++++--- .../expectations/test_expectations_json.pks | 17 +++ 16 files changed, 245 insertions(+), 181 deletions(-) create mode 100644 source/expectations/data_values/ut_json_data_diff_tmp.sql diff --git a/docs/userguide/advanced_data_comparison.md b/docs/userguide/advanced_data_comparison.md index 87d3f5722..4cc4172ed 100644 --- a/docs/userguide/advanced_data_comparison.md +++ b/docs/userguide/advanced_data_comparison.md @@ -431,101 +431,3 @@ Finished in .046193 seconds -## Json Data-types - -### Syntax - -``` - ut.expect( a_actual {data-type} ).to_( equal( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); - ut.expect( a_actual {data-type} ).not_to( equal( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]) ); - ut.expect( a_actual {data-type} ).to_equal( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); - ut.expect( a_actual {data-type} ).not_to_equal( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]] ); - ut.expect( a_actual {data-type} ).to_( contain( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); - ut.expect( a_actual {data-type} ).not_to( contain( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]) ); - ut.expect( a_actual {data-type} ).to_contain( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); - ut.expect( a_actual {data-type} ).not_to_contain( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); -``` - -`extended_option` can be one of: - -- `include(a_items varchar2)` - item or comma separated list of items to include - -- `exclude(a_items varchar2)` - item or comma separated list of items to exclude - -- `include(a_items ut_varchar2_list)` - table of items to include - -- `exclude(a_items ut_varchar2_list)` - table of items to exclude - -- `to_have_count(a_items ut_varchar2)` - Items to be included in count - -- `to_have_count(a_items ut_varchar2_list)` - table of items to be included in count - - -Each item in the comma separated list can be: - -- a column name of cursor to be compared -- an attribute name of object type to be compared -- an attribute name of object type within a table of objects to be compared -- Include and exclude option will not support implicit colum names that starts with single quota, or in fact any other special characters e.g. <, >, & - -Each element in `ut_varchar2_list` nested table can be an item or a comma separated list of items. - -When specifying column/attribute names, keep in mind that the names are **case sensitive**. - -### Excluding elements from data comparison - -Consider the following examples - -```sql -procedure test_cur_skip_columns_eq is - l_expected sys_refcursor; - l_actual sys_refcursor; -begin - open l_expected for select 'text' ignore_me, d.* from user_tables d; - open l_actual for select sysdate "ADate", d.* from user_tables d; - ut.expect( l_actual ).to_equal( l_expected ).exclude( 'IGNORE_ME,ADate' ); -end; - -procedure test_cur_skip_columns_cn is - l_expected sys_refcursor; - l_actual sys_refcursor; -begin - open l_expected for select 'text' ignore_me, d.* from user_tables d where rownum = 1; - open l_actual for select sysdate "ADate", d.* from user_tables d; - ut.expect( l_actual ).to_contain( l_expected ).exclude( 'IGNORE_ME,ADate' ); -end; -``` - -Columns 'ignore_me' and "ADate" will get excluded from cursor comparison. -The cursor data is equal or includes expected, when those columns are excluded. - -This option is useful in scenarios, when you need to exclude incomparable/unpredictable column data like CREATE_DATE of a record that is maintained by default value on a table column. - -### Selecting columns for data comparison - -Consider the following example - -```sql -procedure include_col_as_csv_eq is - l_actual sys_refcursor; - l_expected sys_refcursor; -begin - open l_expected for select rownum as rn, 'a' as "A_Column", 'x' SOME_COL from dual a connect by level < 4; - open l_actual for select rownum as rn, 'a' as "A_Column", 'x' SOME_COL, a.* from all_objects a where rownum < 4; - ut.expect( l_actual ).to_equal( l_expected ).include( 'RN,A_Column,SOME_COL' ); -end; - -procedure include_col_as_csv_cn is - l_actual sys_refcursor; - l_expected sys_refcursor; -begin - open l_expected for select rownum as rn, 'a' as "A_Column", 'x' SOME_COL from dual a connect by level < 4; - open l_actual for select rownum as rn, 'a' as "A_Column", 'x' SOME_COL, a.* from all_objects a where rownum < 6; - ut.expect( l_actual ).to_contain( l_expected ).include( 'RN,A_Column,SOME_COL' ); -end; -``` - -### Counting elements of json - -`to_have_count` supports json objects,scalars and arrays. For json objects it will return a number of keys, for arrays number of elements and for scalar always 1. As an argument into matcher you can pass a json path to element that will be count. If this is null it will take a current object. - diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index 9a3dc2236..683eb337c 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -1189,16 +1189,6 @@ utPLSQL is capable of comparing json data-types on Oracle 12.2 and above. - During comparison of json arrays the index of element is taken into account - To compare json you have to make sure its type of `json_element_t` or its subtypes -utPLSQL offers advanced data-comparison options, for comparing json data-types. The options allow you to: - -- define json path query to exclude from comparison -- define json path query to include in comparison -- and more ... - -For details on available options and how to use them, read the [advanced data comparison](advanced_data_comparison.md) guide. - - - # Negating a matcher Expectations provide a very convenient way to perform a check on a negated matcher. diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index 5a1559a1a..9b9569737 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -624,7 +624,7 @@ create or replace package body ut_compound_data_helper is end; end; - function get_json_diffs(a_act_json_data ut_json_leaf_tab,a_exp_json_data ut_json_leaf_tab) return tt_json_diff_tab is + function compare_json_data(a_act_json_data ut_json_leaf_tab,a_exp_json_data ut_json_leaf_tab) return tt_json_diff_tab is l_result_diff tt_json_diff_tab := tt_json_diff_tab(); begin with differences as ( @@ -669,16 +669,41 @@ create or replace package body ut_compound_data_helper is return l_result_diff; end; + function insert_json_diffs(a_diff_id raw, a_act_json_data ut_json_leaf_tab,a_exp_json_data ut_json_leaf_tab) return integer is + l_diffs tt_json_diff_tab := compare_json_data(a_act_json_data,a_exp_json_data); + begin + forall i in 1..l_diffs.count + insert into ut_json_data_diff_tmp + (diff_id, difference_type,act_element_name,act_element_value,act_json_type,act_access_path, + exp_element_name,exp_element_value,exp_json_type,exp_access_path) + values + (a_diff_id,l_diffs(i).difference_type, + l_diffs(i).act_element_name,l_diffs(i).act_element_value,l_diffs(i).act_json_type, l_diffs(i).act_access_path, + l_diffs(i).exp_element_name,l_diffs(i).exp_element_value,l_diffs(i).exp_json_type,l_diffs(i).exp_access_path); + + return l_diffs.count; --TODO : na sqlrowcount + end; + function get_json_diffs_type(a_diffs_all tt_json_diff_tab) return tt_json_diff_type_tab is l_diffs_summary tt_json_diff_type_tab := tt_json_diff_type_tab(); begin - select difference_type,count(1) + select d.difference_type,count(1) bulk collect into l_diffs_summary from table(a_diffs_all) d group by d.difference_type; return l_diffs_summary; end; + + function get_json_diffs_tmp(a_diff_id raw) return tt_json_diff_tab is + l_diffs tt_json_diff_tab; + begin + select difference_type,act_element_name,act_element_value,act_json_type,act_access_path, + exp_element_name,exp_element_value,exp_json_type,exp_access_path + bulk collect into l_diffs + from ut_json_data_diff_tmp where diff_id = a_diff_id; + return l_diffs; + end; begin g_anytype_name_map(dbms_types.typecode_date) := 'DATE'; diff --git a/source/expectations/data_values/ut_compound_data_helper.pks b/source/expectations/data_values/ut_compound_data_helper.pks index adb91d296..9ab74b224 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pks +++ b/source/expectations/data_values/ut_compound_data_helper.pks @@ -125,7 +125,12 @@ create or replace package ut_compound_data_helper authid definer is */ function type_no_length ( a_type_name varchar2) return boolean; - function get_json_diffs(a_act_json_data ut_json_leaf_tab,a_exp_json_data ut_json_leaf_tab) return tt_json_diff_tab; + function compare_json_data(a_act_json_data ut_json_leaf_tab,a_exp_json_data ut_json_leaf_tab) return tt_json_diff_tab; + + function insert_json_diffs(a_diff_id raw, a_act_json_data ut_json_leaf_tab,a_exp_json_data ut_json_leaf_tab) return integer; + + function get_json_diffs_tmp(a_diff_id raw) return tt_json_diff_tab; + function get_json_diffs_type(a_diffs_all tt_json_diff_tab) return tt_json_diff_type_tab; diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb index 57b6e46ba..9c211bbd7 100644 --- a/source/expectations/data_values/ut_data_value_json.tpb +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -20,11 +20,12 @@ create or replace type body ut_data_value_json as constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result is begin - self.is_data_null := case when a_value is null then 1 else 0 end; - self.data_value := case when a_value is null then null else a_value.to_clob end; - self.self_type := $$plsql_unit; - self.data_type := 'json'; - self.json_tree := ut_json_tree_details(a_value); + self.is_data_null := case when a_value is null then 1 else 0 end; + self.data_value := case when a_value is null then null else a_value.to_clob end; + self.self_type := $$plsql_unit; + self.data_type := 'json'; + self.json_tree := ut_json_tree_details(a_value); + self.data_id := sys_guid(); return; end; @@ -49,7 +50,7 @@ create or replace type body ut_data_value_json as l_result_string varchar2(32767); l_other ut_data_value_json; l_self ut_data_value_json := self; - + l_diff_id ut_compound_data_helper.t_hash; c_max_rows integer := ut_utils.gc_diff_max_rows; l_diffs ut_compound_data_helper.tt_json_diff_tab; l_message varchar2(32767); @@ -72,14 +73,14 @@ create or replace type body ut_data_value_json as when ut_compound_data_helper.gc_json_missing then case - when a_json_diff.act_element_name is not null then 'Missing property "'||a_json_diff.act_element_name||'"' - when a_json_diff.exp_element_name is not null then 'Extra property "'||a_json_diff.exp_element_name||'"' + when a_json_diff.act_element_name is not null then q'[Missing property ']'||a_json_diff.act_element_name||q'[']' + when a_json_diff.exp_element_name is not null then q'[Extra property ']'||a_json_diff.exp_element_name||q'[']' else 'Unknown' end when ut_compound_data_helper.gc_json_type - then 'Actual type is "'||a_json_diff.act_json_type||'" was expected to be "'||a_json_diff.exp_json_type||'"' + then q'[Actual type is ']'||a_json_diff.act_json_type||q'[' was expected to be ']'||a_json_diff.exp_json_type||q'[']' when ut_compound_data_helper.gc_json_notequal - then 'Actual value is "'||a_json_diff.act_element_value||'" was expected to be "'||a_json_diff.exp_element_value||'"' + then q'[Actual value is ']'||a_json_diff.act_element_value||q'[' was expected to be ']'||a_json_diff.exp_element_value||q'[']' else 'Unknown' end || ' on path :'||nvl(a_json_diff.act_access_path,a_json_diff.exp_access_path); end; @@ -89,11 +90,10 @@ create or replace type body ut_data_value_json as raise value_error; end if; l_other := treat(a_other as ut_data_value_json); + l_diff_id := ut_compound_data_helper.get_hash(self.data_id||l_other.data_id); if not l_self.is_null and not l_other.is_null then - l_diffs := ut_compound_data_helper.get_json_diffs( - l_other.json_tree.json_tree_info, - l_self.json_tree.json_tree_info); + l_diffs := ut_compound_data_helper.get_json_diffs_tmp(l_diff_id); l_message:= chr(10)||'Found: '||l_diffs.count|| ' differences' || case when l_diffs.count > c_max_rows then ', showing first '||c_max_rows else null end||chr(10); @@ -114,32 +114,43 @@ create or replace type body ut_data_value_json as dbms_lob.freetemporary(l_result); return l_result_string; end; - overriding member function compare_implementation(a_other ut_data_value) return integer is - begin - return compare_implementation( a_other, null ); + l_self ut_data_value_json := self; + l_other ut_data_value := a_other; + begin + return l_self.compare_implementation( l_other, null ); end; - - member function compare_implementation(a_other ut_data_value,a_match_options ut_matcher_options) return integer is + + member function compare_implementation(a_other in ut_data_value,a_match_options ut_matcher_options) return + integer is l_result integer; l_other ut_data_value_json; + l_diff_id ut_compound_data_helper.t_hash; begin if a_other is of (ut_data_value_json) then l_other := treat(a_other as ut_data_value_json); - --select case when json_equal(self.data_value, l_other.data_value) then 0 else 1 end - --into l_result - --from dual; - l_result := case when self.json_tree.equals( l_other.json_tree, a_match_options ) then 0 else 1 end; + l_diff_id := ut_compound_data_helper.get_hash(self.data_id||l_other.data_id); + l_result := case when ut_compound_data_helper.insert_json_diffs( + l_diff_id,self.json_tree.json_tree_info,l_other.json_tree.json_tree_info) > 0 then 1 else 0 end; end if; - return l_result; end; + member function get_elements_count return integer is + begin + return json_element_t.parse(self.data_value).get_size; + end; + + member function get_json_count_info return varchar2 is + begin + return self.data_type||' [ count = '||self.get_elements_count||' ]'; + end; + overriding member function get_object_info return varchar2 is begin return self.data_type; end; - + end; / diff --git a/source/expectations/data_values/ut_data_value_json.tps b/source/expectations/data_values/ut_data_value_json.tps index a89cf19cc..30a30d3c7 100644 --- a/source/expectations/data_values/ut_data_value_json.tps +++ b/source/expectations/data_values/ut_data_value_json.tps @@ -24,6 +24,8 @@ create or replace type ut_data_value_json under ut_compound_data_value( overriding member function diff( a_other ut_data_value, a_match_options ut_matcher_options ) return varchar2, overriding member function compare_implementation(a_other ut_data_value) return integer, member function compare_implementation(a_other ut_data_value,a_match_options ut_matcher_options) return integer, + member function get_elements_count return integer, + member function get_json_count_info return varchar2, overriding member function get_object_info return varchar2 ) / diff --git a/source/expectations/data_values/ut_json_data_diff_tmp.sql b/source/expectations/data_values/ut_json_data_diff_tmp.sql new file mode 100644 index 000000000..73966ce0c --- /dev/null +++ b/source/expectations/data_values/ut_json_data_diff_tmp.sql @@ -0,0 +1,25 @@ +create global temporary table ut_json_data_diff_tmp( + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 utPLSQL Project + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + diff_id raw(128), + difference_type varchar2(250), + act_element_name varchar2(2000), + act_element_value clob, + act_json_type varchar2(100), + act_access_path varchar2(4000), + exp_element_name varchar2(2000), + exp_element_value clob, + exp_json_type varchar2(2000), + exp_access_path varchar2(4000) +) on commit preserve rows; diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index a3610a439..77cd29a1a 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -1,21 +1,5 @@ create or replace type body ut_json_tree_details as - --TODO : Equal to be reviewed specially order of arrays and order of objects - member function equals( a_other ut_json_tree_details, a_match_options ut_matcher_options ) return boolean is - l_diffs integer; - begin - select count(1) into l_diffs - from table(self.json_tree_info) a - full outer join table(a_other.json_tree_info) e - on decode(a.parent_name,e.parent_name,1,0)= 1 - and decode(a.element_name,e.element_name,1,0) = 1 - and a.json_type = e.json_type - and a.hierarchy_level = e.hierarchy_level - and decode(a.element_value,e.element_value,1,0) = 1 - where a.element_name is null or e.element_name is null; - return l_diffs = 0; - end; - member function get_json_type(a_json_piece json_element_t) return varchar2 is begin return case @@ -44,8 +28,8 @@ create or replace type body ut_json_tree_details as when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); when l_json_el.is_true then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); when l_json_el.is_false then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); - when l_json_el.is_date then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR'); - when a_json_piece.is_timestamp then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); + when l_json_el.is_date then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR'); + when a_json_piece.is_timestamp then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); else null; end case; return l_val; @@ -61,8 +45,8 @@ create or replace type body ut_json_tree_details as when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); when l_json_el.is_true then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); when l_json_el.is_false then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); - when l_json_el.is_date then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR'); - when a_json_piece.is_timestamp then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); + when l_json_el.is_date then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR'); + when a_json_piece.is_timestamp then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); else null; end case; return l_val; diff --git a/source/expectations/data_values/ut_json_tree_details.tps b/source/expectations/data_values/ut_json_tree_details.tps index 3da95f291..1938c1c6e 100644 --- a/source/expectations/data_values/ut_json_tree_details.tps +++ b/source/expectations/data_values/ut_json_tree_details.tps @@ -1,4 +1,4 @@ -create or replace type ut_json_tree_details force as object ( +create or replace type ut_json_tree_details as object ( /* utPLSQL - Version 3 Copyright 2016 - 2018 utPLSQL Project @@ -16,7 +16,6 @@ create or replace type ut_json_tree_details force as object ( limitations under the License. */ json_tree_info ut_json_leaf_tab, - member function equals(a_other ut_json_tree_details, a_match_options ut_matcher_options) return boolean, member function get_json_type(a_json_piece json_element_t) return varchar2, member function get_json_value(a_json_piece json_object_t,a_key varchar2) return varchar2, member function get_json_value(a_json_piece json_array_t,a_key integer) return varchar2, diff --git a/source/expectations/matchers/ut_have_count.tpb b/source/expectations/matchers/ut_have_count.tpb index 135a948d5..1cca8a228 100644 --- a/source/expectations/matchers/ut_have_count.tpb +++ b/source/expectations/matchers/ut_have_count.tpb @@ -28,6 +28,8 @@ create or replace type body ut_have_count as begin if a_actual is of(ut_data_value_refcursor) and ( treat (a_actual as ut_data_value_refcursor).compound_type != 'object') then l_result := ( self.expected = treat(a_actual as ut_data_value_refcursor).elements_count ); + elsif a_actual is of(ut_data_value_json) then + l_result := ( self.expected = treat(a_actual as ut_data_value_json).get_elements_count ); else l_result := (self as ut_matcher).run_matcher(a_actual); end if; @@ -36,12 +38,16 @@ create or replace type body ut_have_count as overriding member function failure_message(a_actual ut_data_value) return varchar2 is begin - return 'Actual: (' || a_actual.get_object_info()||') was expected to have [ count = '||ut_utils.to_string(self.expected)||' ]'; + return 'Actual: (' || case when a_actual is of (ut_data_value_json) then + treat(a_actual as ut_data_value_json).get_json_count_info() else a_actual.get_object_info() end|| + ') was expected to have [ count = '||ut_utils.to_string(self.expected)||' ]'; end; overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2 is begin - return 'Actual: ' || a_actual.get_object_info()||' was expected not to have [ count = '||ut_utils.to_string(self.expected)||' ]'; + return 'Actual: ' || case when a_actual is of (ut_data_value_json) then + treat(a_actual as ut_data_value_json).get_json_count_info() else a_actual.get_object_info() end|| + ' was expected not to have [ count = '||ut_utils.to_string(self.expected)||' ]'; end; end; diff --git a/source/expectations/ut_expectation_json.tpb b/source/expectations/ut_expectation_json.tpb index 62a459ffc..d015aa5f4 100644 --- a/source/expectations/ut_expectation_json.tpb +++ b/source/expectations/ut_expectation_json.tpb @@ -47,5 +47,15 @@ create or replace type body ut_expectation_json as return l_result; end; + member procedure to_have_count(self in ut_expectation_json, a_expected integer) is + begin + self.to_( ut_have_count(a_expected) ); + end; + + member procedure not_to_have_count(self in ut_expectation_json, a_expected integer) is + begin + self.not_to( ut_have_count(a_expected) ); + end; + end; / diff --git a/source/expectations/ut_expectation_json.tps b/source/expectations/ut_expectation_json.tps index 4e840f2c4..d032d7851 100644 --- a/source/expectations/ut_expectation_json.tps +++ b/source/expectations/ut_expectation_json.tps @@ -1,4 +1,4 @@ -create or replace type ut_expectation_json force under ut_expectation( +create or replace type ut_expectation_json under ut_expectation( /* utPLSQL - Version 3 Copyright 2016 - 2018 utPLSQL Project @@ -22,6 +22,8 @@ create or replace type ut_expectation_json force under ut_expectation( member procedure to_be_empty(self in ut_expectation_json), member procedure not_to_be_empty(self in ut_expectation_json), member function to_equal(a_expected json_element_t , a_nulls_are_equal boolean := null) return ut_expectation_json, - member function not_to_equal(a_expected json_element_t , a_nulls_are_equal boolean := null) return ut_expectation_json + member function not_to_equal(a_expected json_element_t , a_nulls_are_equal boolean := null) return ut_expectation_json, + member procedure to_have_count(self in ut_expectation_json, a_expected integer), + member procedure not_to_have_count(self in ut_expectation_json, a_expected integer) ) / diff --git a/source/install.sql b/source/install.sql index 9d4c94ef9..2c7a6916d 100644 --- a/source/install.sql +++ b/source/install.sql @@ -192,6 +192,7 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema --expectations and matchers @@install_component.sql 'expectations/data_values/ut_compound_data_tmp.sql' @@install_component.sql 'expectations/data_values/ut_compound_data_diff_tmp.sql' +@@install_component.sql 'expectations/data_values/ut_json_data_diff_tmp.sql' @@install_component.sql 'expectations/data_values/ut_compound_data_value.tps' @@install_component.sql 'expectations/data_values/ut_json_leaf.tps' @@install_component.sql 'expectations/data_values/ut_json_leaf_tab.tps' diff --git a/source/uninstall_objects.sql b/source/uninstall_objects.sql index 8a124e001..3d270ecea 100644 --- a/source/uninstall_objects.sql +++ b/source/uninstall_objects.sql @@ -163,6 +163,8 @@ drop table ut_compound_data_tmp; drop table ut_compound_data_diff_tmp; +drop table ut_json_data_diff_tmp; + drop package ut_annotation_manager; drop package ut_annotation_parser; diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index c3b01d8e2..127dd8632 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -28,30 +28,30 @@ create or replace package body test_expectations_json is l_actual_message varchar2(32767); begin -- Arrange - l_actual := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); - l_expected := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thron\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true,"object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"], "Clarke Peters": null}'); + l_expected := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + l_actual := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thron\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true,"object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"], "Clarke Peters": null}'); --Act ut3.ut.expect( l_actual ).to_equal( l_expected ); --Assert - l_expected_message := q'[%%Actual type is "array" was expected to be "object" on path :$.Amy Ryan -%Missing property "Alexander Skarsg?rd" on path :$.Alexander Skarsg?rd -%Extra property "Alexander Skarsgard" on path :$.Alexander Skarsgard -%Missing property "Alice Farmer" on path :$.Alice Farmer -%Extra property "Clarke Peters" on path :$.Clarke Peters -%Actual value is "True Blood" was expected to be "Big Love" on path :$.Annie Fitzgerald[0] -%Actual value is "Big Love" was expected to be "True Blood" on path :$.Annie Fitzgerald[1] -%Missing property ""The Sopranos"" on path :$.Annie Fitzgerald[2] -%Missing property ""Oz"" on path :$.Annie Fitzgerald[3] -%Actual type is "string" was expected to be "number" on path :$.Aidan Gillen.int -%Missing property "otherint" on path :$.Aidan Gillen.otherint -%Actual type is "string" was expected to be "boolean" on path :$.Aidan Gillen.aboolean -%Actual value is "false" was expected to be "true" on path :$.Aidan Gillen.boolean -%Actual value is "Game of Thrones" was expected to be "Game of Thron"es" on path :$.Aidan Gillen.array[0] -%Extra property "object1" on path :$.Aidan Gillen.object.object1 -%Extra property "object2" on path :$.Aidan Gillen.object.object2 -%Extra property "object3" on path :$.Aidan Gillen.object.object3 -%Extra property "object4" on path :$.Aidan Gillen.object.object4]'; + l_expected_message := q'[%Actual type is 'array' was expected to be 'object' on path :$.Amy Ryan +%Missing property 'Alexander Skarsg?rd' on path :$.Alexander Skarsg?rd +%Extra property 'Alexander Skarsgard' on path :$.Alexander Skarsgard +%Missing property 'Alice Farmer' on path :$.Alice Farmer +%Extra property 'Clarke Peters' on path :$.Clarke Peters +%Actual value is 'True Blood' was expected to be 'Big Love' on path :$.Annie Fitzgerald[0] +%Actual value is 'Big Love' was expected to be 'True Blood' on path :$.Annie Fitzgerald[1] +%Missing property '"The Sopranos"' on path :$.Annie Fitzgerald[2] +%Missing property '"Oz"' on path :$.Annie Fitzgerald[3] +%Actual type is 'string' was expected to be 'number' on path :$.Aidan Gillen.int +%Missing property 'otherint' on path :$.Aidan Gillen.otherint +%Actual type is 'string' was expected to be 'boolean' on path :$.Aidan Gillen.aboolean +%Actual value is 'false' was expected to be 'true' on path :$.Aidan Gillen.boolean +%Actual value is 'Game of Thrones' was expected to be 'Game of Thron"es' on path :$.Aidan Gillen.array[0] +%Extra property 'object1' on path :$.Aidan Gillen.object.object1 +%Extra property 'object2' on path :$.Aidan Gillen.object.object2 +%Extra property 'object3' on path :$.Aidan Gillen.object.object3 +%Extra property 'object4' on path :$.Aidan Gillen.object.object4%]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert ut.expect(l_actual_message).to_be_like(l_expected_message); @@ -187,5 +187,88 @@ create or replace package body test_expectations_json is ut.expect(l_actual_message).to_be_like(l_expected_message); end; + procedure to_have_count as + l_actual json_element_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_actual := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + + --Act + ut3.ut.expect( l_actual ).to_have_count( 6 ); + + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + + end; + + procedure fail_to_have_count + as + l_actual json_element_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_actual := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + + --Act + ut3.ut.expect( l_actual ).to_have_count( 2 ); + --Assert + l_expected_message := q'[%Actual: (json [ count = 6 ]) was expected to have [ count = 2 ]%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + + end; + + procedure not_to_have_count + as + l_actual json_element_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_actual := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + + --Act + ut3.ut.expect( l_actual ).not_to_have_count( 7 ); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure fail_not_to_have_count + as + l_actual json_element_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_actual := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + + --Act + ut3.ut.expect( l_actual ).not_to_have_count( 6 ); + --Assert + l_expected_message := q'[%Actual: json [ count = 6 ] was expected not to have [ count = 6 ]%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure to_have_count_array + as + l_actual json_element_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_actual := json_element_t.parse('["Game of Thrones","The Wire"]'); + + --Act + ut3.ut.expect( l_actual ).to_have_count( 2 ); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + end; / diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index 55e29f9e6..15da48e7b 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -37,5 +37,22 @@ create or replace package test_expectations_json is --%test( Fail json string is not empty) procedure fail_not_empty_json; + --%test( Json object to have count ) + procedure to_have_count; + + --%test( Fail Json object to have count) + procedure fail_to_have_count; + + --%test( Json object not to have count) + procedure not_to_have_count; + + --%test( Fail Json object not to have count) + procedure fail_not_to_have_count; + + --%test( Json object to have count on array) + procedure to_have_count_array; + + + end; / From 8e224c043f7a8a9878306183c6827f9de5f29fdc Mon Sep 17 00:00:00 2001 From: lwasylow Date: Thu, 23 May 2019 15:10:46 +0100 Subject: [PATCH 06/24] Added more complex tests. Added test when extract a piece from two different jsons. Small fixes to code on same hierarchy parent arrays --- .../data_values/ut_compound_data_helper.pkb | 1 + .../data_values/ut_data_value_json.tpb | 1 + .../expectations/data_values/ut_json_leaf.tpb | 7 +- .../expectations/data_values/ut_json_leaf.tps | 5 +- .../data_values/ut_json_tree_details.tpb | 14 +- .../data_values/ut_json_tree_details.tps | 2 +- .../expectations/test_expectations_json.pkb | 221 +++++++++++++++++- .../expectations/test_expectations_json.pks | 6 +- 8 files changed, 243 insertions(+), 14 deletions(-) diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index 9b9569737..75a640302 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -643,6 +643,7 @@ create or replace package body ut_compound_data_helper is from table(a_act_json_data) a full outer join table(a_exp_json_data) e on decode(a.parent_name,e.parent_name,1,0)= 1 + and decode(a.parent_path,e.parent_path,1,0)= 1 and ( case when a.parent_type = 'object' or e.parent_type = 'object' then decode(a.element_name,e.element_name,1,0) diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb index 9c211bbd7..5c100791a 100644 --- a/source/expectations/data_values/ut_data_value_json.tpb +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -89,6 +89,7 @@ create or replace type body ut_data_value_json as if not a_other is of (ut_data_value_json) then raise value_error; end if; + dbms_lob.createtemporary(l_result, true); l_other := treat(a_other as ut_data_value_json); l_diff_id := ut_compound_data_helper.get_hash(self.data_id||l_other.data_id); diff --git a/source/expectations/data_values/ut_json_leaf.tpb b/source/expectations/data_values/ut_json_leaf.tpb index f6db72a16..83f1009ef 100644 --- a/source/expectations/data_values/ut_json_leaf.tpb +++ b/source/expectations/data_values/ut_json_leaf.tpb @@ -3,7 +3,7 @@ create or replace type body ut_json_leaf as member procedure init( self in out nocopy ut_json_leaf, a_element_name varchar2, a_element_value varchar2,a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, - a_parent_type varchar2, a_array_element integer:=0) is + a_parent_type varchar2, a_array_element integer:=0, a_parent_path varchar2) is begin self.element_name := a_element_name; self.element_value := a_element_value; @@ -14,16 +14,17 @@ create or replace type body ut_json_leaf as self.json_type := a_json_type; self.is_array_element := a_array_element; self.parent_type := a_parent_type; + self.parent_path := a_parent_path; end; constructor function ut_json_leaf( self in out nocopy ut_json_leaf, a_element_name varchar2, a_element_value varchar2,a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, - a_parent_type varchar2, a_array_element integer:=0) + a_parent_type varchar2, a_array_element integer:=0, a_parent_path varchar2) return self as result is begin init(a_element_name,a_element_value,a_parent_name, a_access_path, a_hierarchy_level, a_index_position, - a_json_type,a_parent_type,a_array_element); + a_json_type,a_parent_type,a_array_element, a_parent_path); return; end; diff --git a/source/expectations/data_values/ut_json_leaf.tps b/source/expectations/data_values/ut_json_leaf.tps index 46b485d09..858b3c3c8 100644 --- a/source/expectations/data_values/ut_json_leaf.tps +++ b/source/expectations/data_values/ut_json_leaf.tps @@ -26,16 +26,17 @@ create or replace type ut_json_leaf force authid current_user as object ( json_type varchar2(2000), is_array_element integer, parent_type varchar2(2000), + parent_path varchar2(4000), member procedure init(self in out nocopy ut_json_leaf, a_element_name varchar2, a_element_value varchar2,a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, - a_parent_type varchar2, a_array_element integer:=0), + a_parent_type varchar2, a_array_element integer:=0, a_parent_path varchar2), constructor function ut_json_leaf( self in out nocopy ut_json_leaf, a_element_name varchar2, a_element_value varchar2,a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, - a_parent_type varchar2, a_array_element integer:=0) + a_parent_type varchar2, a_array_element integer:=0, a_parent_path varchar2) return self as result ) / diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index 77cd29a1a..7620a42a3 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -59,21 +59,22 @@ create or replace type body ut_json_tree_details as member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, - a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0) is + a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0, a_parent_path varchar2) is begin self.json_tree_info.extend; self.json_tree_info(self.json_tree_info.last) := ut_json_leaf(a_element_name,a_element_value,a_parent_name,a_access_path, - a_hierarchy_level, a_index_position,a_json_type, a_parent_type, a_array_element); + a_hierarchy_level, a_index_position,a_json_type, a_parent_type, a_array_element, a_parent_path); end; member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_object_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as l_keys json_key_list; + l_object json_element_t; begin l_keys := coalesce(a_json_piece.get_keys,json_key_list()); for indx in 1 .. l_keys.count - loop + loop add_json_leaf(l_keys(indx), get_json_value(a_json_piece,l_keys(indx)), a_parent_name, @@ -81,7 +82,9 @@ create or replace type body ut_json_tree_details as a_hierarchy_level, indx, get_json_type(a_json_piece.get (l_keys(indx))), - 'object' + 'object', + 0, + a_access_path ); case get_json_type(a_json_piece.get(l_keys(indx))) when 'array' then @@ -118,7 +121,8 @@ create or replace type body ut_json_tree_details as indx, get_json_type(l_array.get (indx)), 'array', - 1 + 1, + a_access_path ); case get_json_type(l_array.get (indx)) when 'array' then diff --git a/source/expectations/data_values/ut_json_tree_details.tps b/source/expectations/data_values/ut_json_tree_details.tps index 1938c1c6e..e6f578b87 100644 --- a/source/expectations/data_values/ut_json_tree_details.tps +++ b/source/expectations/data_values/ut_json_tree_details.tps @@ -22,7 +22,7 @@ create or replace type ut_json_tree_details as object ( member function get_json_size(a_json_piece json_object_t) return integer, member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, - a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0), + a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0,a_parent_path varchar2), member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_object_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ), member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_array_t, diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index 127dd8632..dd1e4afc9 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -11,8 +11,78 @@ create or replace package body test_expectations_json is l_actual json_element_t; begin -- Arrange - l_expected := json_element_t.parse('{"name1":"value1","name2":"value2"}'); - l_actual := json_element_t.parse('{"name1":"value1","name2":"value2"}'); + l_expected := json_element_t.parse(' { + "Actors": [ + { + "name": "Tom Cruise", + "age": 56, + "Born At": "Syracuse, NY", + "Birthdate": "July 3, 1962", + "photo": "https://jsonformatter.org/img/tom-cruise.jpg", + "wife": null, + "weight": 67.5, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Suri", + "Isabella Jane", + "Connor" + ] + }, + { + "name": "Robert Downey Jr.", + "age": 53, + "Born At": "New York City, NY", + "Birthdate": "April 4, 1965", + "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", + "wife": "Susan Downey", + "weight": 77.1, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Indio Falconer", + "Avri Roel", + "Exton Elias" + ] + } + ] + }'); + l_actual := json_element_t.parse(' { + "Actors": [ + { + "name": "Tom Cruise", + "age": 56, + "Born At": "Syracuse, NY", + "Birthdate": "July 3, 1962", + "photo": "https://jsonformatter.org/img/tom-cruise.jpg", + "wife": null, + "weight": 67.5, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Suri", + "Isabella Jane", + "Connor" + ] + }, + { + "name": "Robert Downey Jr.", + "age": 53, + "Born At": "New York City, NY", + "Birthdate": "April 4, 1965", + "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", + "wife": "Susan Downey", + "weight": 77.1, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Indio Falconer", + "Avri Roel", + "Exton Elias" + ] + } + ] + }'); --Act ut3.ut.expect( l_actual ).to_equal( l_actual ); @@ -269,6 +339,153 @@ create or replace package body test_expectations_json is --Assert ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; + + procedure to_diff_json_extract_same + as + l_expected json_object_t; + l_actual json_object_t; + BEGIN + -- Arrange + l_expected := json_object_t.parse(' { + "Actors": [ + { + "name": "Tom Cruise", + "age": 56, + "Born At": "Syracuse, NY", + "Birthdate": "July 3, 1962", + "photo": "https://jsonformatter.org/img/tom-cruise.jpg", + "wife": null, + "weight": 67.5, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Suri", + "Isabella Jane", + "Connor" + ] + }, + { + "name": "Robert Downey Jr.", + "age": 53, + "Born At": "New York City, NY", + "Birthdate": "April 4, 1965", + "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", + "wife": "Susan Downey", + "weight": 77.1, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Indio Falconer", + "Avri Roel", + "Exton Elias" + ] + } + ] + }' + ); + l_actual := json_object_t.parse(' { + "Actors": + { + "name": "Krzystof Jarzyna", + "age": 53, + "Born At": "Szczecin", + "Birthdate": "April 4, 1965", + "photo": "niewidzialny", + "wife": "Susan Downey", + "children": [ + "Indio Falconer", + "Avri Roel", + "Exton Elias" + ] + } + }' + ); + + + --Act + ut3.ut.expect(json_array_t(json_query(l_actual.stringify,'$.Actors.children'))).to_equal(json_array_t(json_query(l_expected + .stringify,'$.Actors[1].children'))); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure to_diff_json_extract_diff + as + l_expected json_object_t; + l_actual json_object_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_expected := json_object_t.parse(' { + "Actors": [ + { + "name": "Tom Cruise", + "age": 56, + "Born At": "Syracuse, NY", + "Birthdate": "July 3, 1962", + "photo": "https://jsonformatter.org/img/tom-cruise.jpg", + "wife": null, + "weight": 67.5, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Suri", + "Isabella Jane", + "Connor" + ] + }, + { + "name": "Robert Downey Jr.", + "age": 53, + "Born At": "New York City, NY", + "Birthdate": "April 4, 1965", + "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", + "wife": "Susan Downey", + "weight": 77.1, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Noemi", + "Avri Roel", + "Exton Elias" + ] + } + ] + }' + ); + l_actual := json_object_t.parse(' { + "Actors": + { + "name": "Krzystof Jarzyna", + "age": 53, + "Born At": "Szczecin", + "Birthdate": "April 4, 1965", + "photo": "niewidzialny", + "wife": "Susan Downey", + "children": [ + "Indio Falconer", + "Avri Roel", + "Exton Elias" + ] + } + }' + ); + + + --Act + ut3.ut.expect(json_array_t(json_query(l_actual.stringify,'$.Actors.children'))).to_equal(json_array_t(json_query(l_expected + .stringify,'$.Actors[1].children'))); + --Assert + l_expected_message := q'[%Actual: json was expected to equal: json +%Diff: +%Found: 1 differences +%1 unequal values +%Actual value is 'Noemi' was expected to be 'Indio Falconer' on path :$[0]%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; end; / diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index 15da48e7b..a747638e8 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -52,7 +52,11 @@ create or replace package test_expectations_json is --%test( Json object to have count on array) procedure to_have_count_array; - + --%test( Two json use plsql function to extract same pieces and compare) + procedure to_diff_json_extract_same; + + --%test( Two json use plsql function to extract diff pieces and compare) + procedure to_diff_json_extract_diff; end; / From 1a17e7a26f7af37650128ad14da25fdc6b364b59 Mon Sep 17 00:00:00 2001 From: lwasylow Date: Thu, 23 May 2019 19:20:24 +0100 Subject: [PATCH 07/24] Update mddocs Fix to json traverse array when object is element of array, the element name is content of object which can be different order but same semantic content. --- docs/userguide/expectations.md | 227 +++- .../data_values/ut_compound_data_helper.pkb | 14 +- .../data_values/ut_json_tree_details.tpb | 16 +- .../expectations/test_expectations_json.pkb | 1186 ++++++++++++++++- .../expectations/test_expectations_json.pks | 12 + 5 files changed, 1384 insertions(+), 71 deletions(-) diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index 683eb337c..9781849d9 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -1178,7 +1178,7 @@ Finished in .048181 seconds -# Comparing json objects +# Comparing Json objects utPLSQL is capable of comparing json data-types on Oracle 12.2 and above. @@ -1189,6 +1189,197 @@ utPLSQL is capable of comparing json data-types on Oracle 12.2 and above. - During comparison of json arrays the index of element is taken into account - To compare json you have to make sure its type of `json_element_t` or its subtypes + + +Some examples of using json data-types in matcher are : + +```sql +create or replace package test_expectations_json is + + --%suite(json expectations) + + --%test(Gives success for identical data) + procedure success_on_same_data +end; +/ + +create or replace package body test_expectations_json is + + procedure success_on_same_data + as + l_expected json_element_t; + l_actual json_element_t; + begin + -- Arrange + l_expected := json_element_t.parse(' { + "Actors": [ + { + "name": "Tom Cruise", + "age": 56, + "Born At": "Syracuse, NY", + "Birthdate": "July 3, 1962", + "photo": "https://jsonformatter.org/img/tom-cruise.jpg", + "wife": null, + "weight": 67.5, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Suri", + "Isabella Jane", + "Connor" + ] + }, + { + "name": "Robert Downey Jr.", + "age": 53, + "Born At": "New York City, NY", + "Birthdate": "April 4, 1965", + "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", + "wife": "Susan Downey", + "weight": 77.1, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Indio Falconer", + "Avri Roel", + "Exton Elias" + ] + } + ] + }'); + l_actual := json_element_t.parse(' { + "Actors": [ + { + "name": "Tom Cruise", + "age": 56, + "Born At": "Syracuse, NY", + "Birthdate": "July 3, 1962", + "photo": "https://jsonformatter.org/img/tom-cruise.jpg", + "wife": null, + "weight": 67.5, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Suri", + "Isabella Jane", + "Connor" + ] + }, + { + "name": "Robert Downey Jr.", + "age": 53, + "Born At": "New York City, NY", + "Birthdate": "April 4, 1965", + "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", + "wife": "Susan Downey", + "weight": 77.1, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Indio Falconer", + "Avri Roel", + "Exton Elias" + ] + } + ] + }'); + + ut3.ut.expect( l_actual ).to_equal( l_actual ); + + end; +end; +/ +``` + +It is possible to use a PL/SQL to extract a piece of JSON and compare it as follow + +```sql +create or replace package test_expectations_json is + + --%suite(json expectations) + + --%test(Gives success for identical pieces of two different jsons) + procedure to_diff_json_extract_same +end; +/ + +create or replace package body test_expectations_json is + procedure to_diff_json_extract_same + as + l_expected json_object_t; + l_actual json_object_t; + BEGIN + -- Arrange + l_expected := json_object_t.parse(' { + "Actors": [ + { + "name": "Tom Cruise", + "age": 56, + "Born At": "Syracuse, NY", + "Birthdate": "July 3, 1962", + "photo": "https://jsonformatter.org/img/tom-cruise.jpg", + "wife": null, + "weight": 67.5, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Suri", + "Isabella Jane", + "Connor" + ] + }, + { + "name": "Robert Downey Jr.", + "age": 53, + "Born At": "New York City, NY", + "Birthdate": "April 4, 1965", + "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", + "wife": "Susan Downey", + "weight": 77.1, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Indio Falconer", + "Avri Roel", + "Exton Elias" + ] + } + ] + }' + ); + l_actual := json_object_t.parse(' { + "Actors": + { + "name": "Krzystof Jarzyna", + "age": 53, + "Born At": "Szczecin", + "Birthdate": "April 4, 1965", + "photo": "niewidzialny", + "wife": "Susan Downey", + "children": [ + "Indio Falconer", + "Avri Roel", + "Exton Elias" + ] + } + }' + ); + + + --Act + ut3.ut.expect(json_array_t(json_query(l_actual.stringify,'$.Actors.children'))).to_equal(json_array_t(json_query(l_expected.stringify,'$.Actors[1].children'))); + + end; +end; +/ +``` + + + + + + + # Negating a matcher Expectations provide a very convenient way to perform a check on a negated matcher. @@ -1224,21 +1415,21 @@ Since NULL is neither *true* nor *false*, both expectations will report failure. The matrix below illustrates the data types supported by different matchers. -| Matcher | blob | boolean | clob | date | number | timestamp | timestamp
with
timezone | timestamp
with
local
timezone | varchar2 | interval
year
to
month | interval
day
to
second | cursor | nested
table
/ varray | object | -| :---------------------- | :--: | :-----: | :--: | :--: | :----: | :-------: | :---------------------------: | :------------------------------------: | :------: | :-----------------------------: | :-----------------------------: | :----: | :-------------------------: | :----: | -| **be_not_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | -| **be_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | -| **be_false** | | X | | | | | | | | | | | | | -| **be_true** | | X | | | | | | | | | | | | | -| **be_greater_than** | | | | X | X | X | X | X | | X | X | | | | -| **be_greater_or_equal** | | | | X | X | X | X | X | | X | X | | | | -| **be_less_or_equal** | | | | X | X | X | X | X | | X | X | | | | -| **be_less_than** | | | | X | X | X | X | X | | X | X | | | | -| **be_between** | | | | X | X | X | X | X | X | X | X | | | | -| **equal** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | -| **contain** | | | | | | | | | | | | X | X | X | -| **match** | | | X | | | | | | X | | | | | | -| **be_like** | | | X | | | | | | X | | | | | | -| **be_empty** | X | | X | | | | | | | | | X | X | | -| **have_count** | | | | | | | | | | | | X | X | | +| Matcher | blob | boolean | clob | date | number | timestamp | timestamp
with
timezone | timestamp
with
local
timezone | varchar2 | interval
year
to
month | interval
day
to
second | cursor | nested
table
/ varray | object | json | +| :---------------------: | :--: | :-----: | :--: | :--: | :----: | :-------: | :---------------------------: | :------------------------------------: | :------: | :-----------------------------: | :-----------------------------: | :----: | :-------------------------: | :----: | :--: | +| **be_not_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +| **be_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +| **be_false** | | X | | | | | | | | | | | | | | +| **be_true** | | X | | | | | | | | | | | | | | +| **be_greater_than** | | | | X | X | X | X | X | | X | X | | | | | +| **be_greater_or_equal** | | | | X | X | X | X | X | | X | X | | | | | +| **be_less_or_equal** | | | | X | X | X | X | X | | X | X | | | | | +| **be_less_than** | | | | X | X | X | X | X | | X | X | | | | | +| **be_between** | | | | X | X | X | X | X | X | X | X | | | | | +| **equal** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +| **contain** | | | | | | | | | | | | X | X | X | | +| **match** | | | X | | | | | | X | | | | | | | +| **be_like** | | | X | | | | | | X | | | | | | | +| **be_empty** | X | | X | | | | | | | | | X | X | | X | +| **have_count** | | | | | | | | | | | | X | X | | X | diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index 75a640302..8e8148d42 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -627,6 +627,7 @@ create or replace package body ut_compound_data_helper is function compare_json_data(a_act_json_data ut_json_leaf_tab,a_exp_json_data ut_json_leaf_tab) return tt_json_diff_tab is l_result_diff tt_json_diff_tab := tt_json_diff_tab(); begin + with differences as ( select case @@ -634,6 +635,11 @@ create or replace package body ut_compound_data_helper is when a.json_type != e.json_type then gc_json_type when (decode(a.element_value,e.element_value,1,0) = 0) then gc_json_notequal else gc_json_unknown end as difference_type, + case + when (a.element_name is null or e.element_name is null) then 1 + when a.json_type != e.json_type then 2 + when (decode(a.element_value,e.element_value,1,0) = 0) then 3 + else 4 end as order_by_type, a.element_name as act_element_name, a.element_value as act_element_value, a.hierarchy_level as act_hierarchy_level, a.index_position as act_index_position, a.json_type as act_json_type, a.access_path as act_access_path, a.parent_name act_par_name, @@ -664,9 +670,11 @@ create or replace package body ut_compound_data_helper is bulk collect into l_result_diff from differences a where not exists ( select 1 from differences b where (a.act_par_name = b.act_element_name and a.act_hierarchy_level - 1 = b.act_hierarchy_level) - or (a.exp_par_name = b.exp_element_name and a.exp_hierarchy_level - 1 = b.exp_hierarchy_level)) - order by nvl(act_hierarchy_level,exp_hierarchy_level),nvl(act_index_position,exp_index_position) nulls first, - nvl(act_element_name,exp_element_name),nvl(act_json_type,exp_json_type) ; + or (a.exp_par_name = b.exp_element_name and a.exp_hierarchy_level - 1 = b.exp_hierarchy_level) + and a.difference_type = gc_json_missing and b.difference_type = gc_json_missing) + order by order_by_type, + nvl(act_hierarchy_level,exp_hierarchy_level),nvl(act_index_position,exp_index_position) nulls first, + nvl(act_element_name,exp_element_name) ; return l_result_diff; end; diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index 7620a42a3..ce213f4ea 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -108,34 +108,38 @@ create or replace type body ut_json_tree_details as member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_array_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as l_array json_array_t; + l_type varchar2(50); + l_name varchar2(4000); begin l_array := a_json_piece; - for indx in 0 .. l_array.get_size - 1 loop - add_json_leaf(l_array.get(indx).stringify, + l_type := get_json_type(l_array.get (indx)); + l_name := case when l_type = 'object' then l_type else l_array.get(indx).stringify end; + + add_json_leaf(l_name, get_json_value(a_json_piece,indx), a_parent_name, a_access_path||'['||indx||']', a_hierarchy_level, indx, - get_json_type(l_array.get (indx)), + l_type, 'array', 1, a_access_path ); - case get_json_type(l_array.get (indx)) + case l_type when 'array' then traverse_array ( treat (l_array.get (indx) as json_array_t), - l_array.get(indx).stringify, + l_name, a_hierarchy_level + 1, a_access_path||'['||indx||']' ); when 'object' then traverse_object( treat (a_json_piece.get (indx) as json_object_t), - l_array.get(indx).stringify, + l_name, a_hierarchy_level+1, a_access_path||'['||indx||']' ); diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index dd1e4afc9..3060203b3 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -11,42 +11,6 @@ create or replace package body test_expectations_json is l_actual json_element_t; begin -- Arrange - l_expected := json_element_t.parse(' { - "Actors": [ - { - "name": "Tom Cruise", - "age": 56, - "Born At": "Syracuse, NY", - "Birthdate": "July 3, 1962", - "photo": "https://jsonformatter.org/img/tom-cruise.jpg", - "wife": null, - "weight": 67.5, - "hasChildren": true, - "hasGreyHair": false, - "children": [ - "Suri", - "Isabella Jane", - "Connor" - ] - }, - { - "name": "Robert Downey Jr.", - "age": 53, - "Born At": "New York City, NY", - "Birthdate": "April 4, 1965", - "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", - "wife": "Susan Downey", - "weight": 77.1, - "hasChildren": true, - "hasGreyHair": false, - "children": [ - "Indio Falconer", - "Avri Roel", - "Exton Elias" - ] - } - ] - }'); l_actual := json_element_t.parse(' { "Actors": [ { @@ -104,24 +68,28 @@ create or replace package body test_expectations_json is --Act ut3.ut.expect( l_actual ).to_equal( l_expected ); --Assert - l_expected_message := q'[%Actual type is 'array' was expected to be 'object' on path :$.Amy Ryan + l_expected_message := q'[%Found: 20 differences +%3 incorrect types,4 unequal values,13 missing properties %Missing property 'Alexander Skarsg?rd' on path :$.Alexander Skarsg?rd %Extra property 'Alexander Skarsgard' on path :$.Alexander Skarsgard %Missing property 'Alice Farmer' on path :$.Alice Farmer %Extra property 'Clarke Peters' on path :$.Clarke Peters -%Actual value is 'True Blood' was expected to be 'Big Love' on path :$.Annie Fitzgerald[0] -%Actual value is 'Big Love' was expected to be 'True Blood' on path :$.Annie Fitzgerald[1] +%Extra property 'one' on path :$.Amy Ryan.one %Missing property '"The Sopranos"' on path :$.Annie Fitzgerald[2] +%Extra property 'two' on path :$.Amy Ryan.two %Missing property '"Oz"' on path :$.Annie Fitzgerald[3] -%Actual type is 'string' was expected to be 'number' on path :$.Aidan Gillen.int %Missing property 'otherint' on path :$.Aidan Gillen.otherint -%Actual type is 'string' was expected to be 'boolean' on path :$.Aidan Gillen.aboolean -%Actual value is 'false' was expected to be 'true' on path :$.Aidan Gillen.boolean -%Actual value is 'Game of Thrones' was expected to be 'Game of Thron"es' on path :$.Aidan Gillen.array[0] %Extra property 'object1' on path :$.Aidan Gillen.object.object1 %Extra property 'object2' on path :$.Aidan Gillen.object.object2 %Extra property 'object3' on path :$.Aidan Gillen.object.object3 -%Extra property 'object4' on path :$.Aidan Gillen.object.object4%]'; +%Extra property 'object4' on path :$.Aidan Gillen.object.object4 +%Actual type is 'array' was expected to be 'object' on path :$.Amy Ryan +%Actual type is 'string' was expected to be 'number' on path :$.Aidan Gillen.int +%Actual type is 'string' was expected to be 'boolean' on path :$.Aidan Gillen.aboolean +%Actual value is 'True Blood' was expected to be 'Big Love' on path :$.Annie Fitzgerald[0] +%Actual value is 'Big Love' was expected to be 'True Blood' on path :$.Annie Fitzgerald[1] +%Actual value is 'false' was expected to be 'true' on path :$.Aidan Gillen.boolean +%Actual value is 'Game of Thrones' was expected to be 'Game of Thron"es' on path :$.Aidan Gillen.array[0]%]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert ut.expect(l_actual_message).to_be_like(l_expected_message); @@ -487,5 +455,1135 @@ create or replace package body test_expectations_json is ut.expect(l_actual_message).to_be_like(l_expected_message); end; + procedure long_json_test + as + l_actual json_element_t; + begin + l_actual := json_element_t.parse('[ + { + "_id": "5ce6dc0c3a11766d5a26f494", + "index": 0, + "guid": "a86b8b2d-216d-4061-bafa-f3820e41efbe", + "isActive": true, + "balance": "$1,754.93", + "picture": "http://placehold.it/32x32", + "age": 39, + "eyeColor": "green", + "name": "Pearlie Lott", + "gender": "female", + "company": "KOG", + "email": "pearlielott@kog.com", + "phone": "+1 (852) 567-2605", + "address": "357 Eldert Street, Benson, Montana, 5484", + "about": "Est officia consectetur reprehenderit fugiat culpa ea commodo aliqua deserunt enim eu. Exercitation adipisicing laboris nisi irure commodo dolor consectetur tempor minim sunt ullamco Lorem occaecat. Irure quis ut Lorem aliquip aute pariatur magna laboris duis veniam qui velit. Pariatur occaecat eu minim adipisicing est do. Occaecat do ipsum ut in enim quis voluptate et. Sit ea irure nulla culpa in eiusmod.\r\n", + "registered": "2018-08-24T12:46:31 -01:00", + "latitude": -22.323554, + "longitude": 139.071611, + "tags": [ + "id", + "do", + "amet", + "magna", + "est", + "veniam", + "voluptate" + ], + "friends": [ + { + "id": 0, + "name": "Tammi Lowe" + }, + { + "id": 1, + "name": "Simpson Miles" + }, + { + "id": 2, + "name": "Hogan Osborne" + } + ], + "greeting": "Hello, Pearlie Lott! You have 2 unread messages.", + "favoriteFruit": "banana" + }, + { + "_id": "5ce6dc0c2b56a6f3271fc272", + "index": 1, + "guid": "2a24b446-d11a-4a52-b6c8-86acba1dc65f", + "isActive": true, + "balance": "$1,176.58", + "picture": "http://placehold.it/32x32", + "age": 30, + "eyeColor": "brown", + "name": "Bertha Mack", + "gender": "female", + "company": "AQUAFIRE", + "email": "berthamack@aquafire.com", + "phone": "+1 (804) 504-2151", + "address": "636 Bouck Court, Cresaptown, Vermont, 5203", + "about": "Ipsum est exercitation excepteur reprehenderit ipsum. Do velit dolore minim ad. Quis amet dolor dolore exercitation sint Lorem. Exercitation nulla magna ut incididunt enim veniam voluptate Lorem velit adipisicing sunt deserunt sunt aute. Ullamco id anim Lorem dolore do labore excepteur et reprehenderit sit adipisicing sunt esse veniam. Anim laborum labore labore incididunt in labore exercitation ad occaecat amet ea quis veniam ut.\r\n", + "registered": "2017-12-29T06:00:27 -00:00", + "latitude": 75.542572, + "longitude": 147.312705, + "tags": [ + "veniam", + "sunt", + "commodo", + "ad", + "enim", + "officia", + "nisi" + ], + "friends": [ + { + "id": 0, + "name": "Riddle Williams" + }, + { + "id": 1, + "name": "Tracy Wagner" + }, + { + "id": 2, + "name": "Morrow Phillips" + } + ], + "greeting": "Hello, Bertha Mack! You have 8 unread messages.", + "favoriteFruit": "banana" + }, + { + "_id": "5ce6dc0c6d8631fbfdd2afc7", + "index": 2, + "guid": "66ca5411-4c88-4347-9972-e1016f628098", + "isActive": false, + "balance": "$2,732.22", + "picture": "http://placehold.it/32x32", + "age": 33, + "eyeColor": "blue", + "name": "Fox Morgan", + "gender": "male", + "company": "PERKLE", + "email": "foxmorgan@perkle.com", + "phone": "+1 (985) 401-3450", + "address": "801 Whitty Lane, Snyderville, Guam, 5253", + "about": "Ex officia eu Lorem velit ullamco qui cupidatat irure sunt ea ad deserunt. Officia est consequat aute labore occaecat aliquip. Velit commodo cillum incididunt cupidatat ad id veniam aute labore tempor qui culpa voluptate dolor. Occaecat in ea id labore exercitation non tempor occaecat laboris aute irure fugiat dolor mollit. Voluptate non proident officia deserunt ex et ullamco aute eiusmod cupidatat consequat elit id.\r\n", + "registered": "2015-04-02T06:40:53 -01:00", + "latitude": -27.612441, + "longitude": -134.005929, + "tags": [ + "occaecat", + "amet", + "eu", + "dolore", + "ad", + "fugiat", + "quis" + ], + "friends": [ + { + "id": 0, + "name": "Case Preston" + }, + { + "id": 1, + "name": "Pollard Dawson" + }, + { + "id": 2, + "name": "Frye Mann" + } + ], + "greeting": "Hello, Fox Morgan! You have 2 unread messages.", + "favoriteFruit": "apple" + }, + { + "_id": "5ce6dc0c0a7fea91e0a1fdf5", + "index": 3, + "guid": "f895a236-fc0d-4c08-b2f0-9d1638dc256d", + "isActive": true, + "balance": "$2,746.32", + "picture": "http://placehold.it/32x32", + "age": 34, + "eyeColor": "green", + "name": "Deleon Tucker", + "gender": "male", + "company": "ZANILLA", + "email": "deleontucker@zanilla.com", + "phone": "+1 (883) 415-2709", + "address": "540 Vandam Street, Chical, Wyoming, 5181", + "about": "Consectetur consectetur sint Lorem non id. Fugiat reprehenderit nulla dolore nisi culpa esse ea. Ad occaecat qui magna proident ex pariatur aliquip adipisicing do aute aute sunt. Aliqua aliqua et exercitation sunt ut adipisicing.\r\n", + "registered": "2017-10-08T09:05:49 -01:00", + "latitude": 34.893845, + "longitude": 110.699256, + "tags": [ + "culpa", + "sunt", + "sit", + "ut", + "eiusmod", + "laboris", + "ullamco" + ], + "friends": [ + { + "id": 0, + "name": "Bernadine Pennington" + }, + { + "id": 1, + "name": "Latoya Bradshaw" + }, + { + "id": 2, + "name": "Iva Caldwell" + } + ], + "greeting": "Hello, Deleon Tucker! You have 7 unread messages.", + "favoriteFruit": "banana" + }, + { + "_id": "5ce6dc0c18bc92716a12a8e4", + "index": 4, + "guid": "6ed45f42-1a2b-48b2-89ce-5fdb2505343b", + "isActive": true, + "balance": "$1,049.96", + "picture": "http://placehold.it/32x32", + "age": 30, + "eyeColor": "blue", + "name": "Schwartz Norman", + "gender": "male", + "company": "UPDAT", + "email": "schwartznorman@updat.com", + "phone": "+1 (826) 404-3309", + "address": "925 Harman Street, Cornucopia, Georgia, 5748", + "about": "Qui Lorem ullamco veniam irure aliquip amet exercitation. Velit nisi id laboris adipisicing in esse adipisicing commodo cillum do exercitation tempor. Consequat tempor dolor minim consequat minim ad do tempor excepteur.\r\n", + "registered": "2014-08-10T08:34:27 -01:00", + "latitude": 27.35547, + "longitude": -77.343791, + "tags": [ + "reprehenderit", + "nisi", + "duis", + "fugiat", + "id", + "non", + "laboris" + ], + "friends": [ + { + "id": 0, + "name": "Dora Combs" + }, + { + "id": 1, + "name": "Emerson Wade" + }, + { + "id": 2, + "name": "Alma Mccormick" + } + ], + "greeting": "Hello, Schwartz Norman! You have 1 unread messages.", + "favoriteFruit": "apple" + }, + { + "_id": "5ce6dc0cb7ae44eb76c3e5fd", + "index": 5, + "guid": "0516df27-73db-42a8-b2c3-d34bd976e031", + "isActive": false, + "balance": "$3,679.94", + "picture": "http://placehold.it/32x32", + "age": 32, + "eyeColor": "brown", + "name": "Christi Oneal", + "gender": "female", + "company": "SUREPLEX", + "email": "christioneal@sureplex.com", + "phone": "+1 (985) 408-3098", + "address": "640 Fayette Street, Dennard, Washington, 7962", + "about": "Dolore fugiat sit non dolore nostrud mollit enim id sint culpa do reprehenderit ad. Velit occaecat incididunt nostrud aliqua incididunt do cillum occaecat laboris quis duis. Non tempor culpa aliquip est est consectetur ullamco elit. Voluptate et sit do et. Amet sit irure eu ex enim nulla anim deserunt ut. Sit aute ea ut fugiat eu tempor Lorem.\r\n", + "registered": "2015-05-10T09:24:56 -01:00", + "latitude": 43.343805, + "longitude": 79.535043, + "tags": [ + "occaecat", + "laboris", + "nulla", + "nisi", + "dolore", + "cillum", + "dolore" + ], + "friends": [ + { + "id": 0, + "name": "Marquez Wiggins" + }, + { + "id": 1, + "name": "Mai Fischer" + }, + { + "id": 2, + "name": "Newman Davenport" + } + ], + "greeting": "Hello, Christi Oneal! You have 8 unread messages.", + "favoriteFruit": "strawberry" + } +]'); + + --Act + ut3.ut.expect( l_actual ).to_equal( l_actual ); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure json_same_diffrent_ord + as + l_expected json_element_t; + l_actual json_element_t; + begin + -- Arrange + l_expected := json_element_t.parse('{ + "records": [ + {"field1": "outer", "field2": "thought"}, + {"field2": "thought", "field1": "outer"} + ] , + "special message": "hello, world!" +}'); + l_actual := json_element_t.parse('{ + "special message": "hello, world!" , + "records": [ + {"field2": "thought" ,"field1": "outer"}, + {"field1": "outer" , "field2": "thought"} + ] +}'); + + --Act + ut3.ut.expect( l_actual ).to_equal( l_expected ); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure long_json_test2 + as + l_actual json_element_t; + begin + l_actual := json_element_t.parse('[ + { + "_id":"5ce6dc0c3a11766d5a26f494", + "index":0, + "guid":"a86b8b2d-216d-4061-bafa-f3820e41efbe", + "isActive":true, + "balance":"$1,754.93", + "picture":"http://placehold.it/32x32", + "age":39, + "eyeColor":"green", + "name":"Pearlie Lott", + "gender":"female", + "company":"KOG", + "email":"pearlielott@kog.com", + "phone":"+1 (852) 567-2605", + "address":"357 Eldert Street, Benson, Montana, 5484", + "about":"Est officia consectetur reprehenderit fugiat culpa ea commodo aliqua deserunt enim eu. Exercitation adipisicing laboris nisi irure commodo dolor consectetur tempor minim sunt ullamco Lorem occaecat. Irure quis ut Lorem aliquip aute pariatur magna laboris duis veniam qui velit. Pariatur occaecat eu minim adipisicing est do. Occaecat do ipsum ut in enim quis voluptate et. Sit ea irure nulla culpa in eiusmod.\r\n", + "registered":"2018-08-24T12:46:31 -01:00", + "latitude":-22.323554, + "longitude":139.071611, + "tags":[ + "id", + "do", + "amet", + "magna", + "est", + "veniam", + "voluptate" + ], + "friends":[ + { + "id":0, + "name":"Tammi Lowe" + }, + { + "id":1, + "name":"Simpson Miles" + }, + { + "id":2, + "name":"Hogan Osborne" + } + ], + "greeting":"Hello, Pearlie Lott! You have 2 unread messages.", + "favoriteFruit":"banana" + }, + { + "_id":"5ce6dc0c2b56a6f3271fc272", + "index":1, + "guid":"2a24b446-d11a-4a52-b6c8-86acba1dc65f", + "isActive":true, + "balance":"$1,176.58", + "picture":"http://placehold.it/32x32", + "age":30, + "eyeColor":"brown", + "name":"Bertha Mack", + "gender":"female", + "company":"AQUAFIRE", + "email":"berthamack@aquafire.com", + "phone":"+1 (804) 504-2151", + "address":"636 Bouck Court, Cresaptown, Vermont, 5203", + "about":"Ipsum est exercitation excepteur reprehenderit ipsum. Do velit dolore minim ad. Quis amet dolor dolore exercitation sint Lorem. Exercitation nulla magna ut incididunt enim veniam voluptate Lorem velit adipisicing sunt deserunt sunt aute. Ullamco id anim Lorem dolore do labore excepteur et reprehenderit sit adipisicing sunt esse veniam. Anim laborum labore labore incididunt in labore exercitation ad occaecat amet ea quis veniam ut.\r\n", + "registered":"2017-12-29T06:00:27 -00:00", + "latitude":75.542572, + "longitude":147.312705, + "tags":[ + "veniam", + "sunt", + "commodo", + "ad", + "enim", + "officia", + "nisi" + ], + "friends":[ + { + "id":0, + "name":"Riddle Williams" + }, + { + "id":1, + "name":"Tracy Wagner" + }, + { + "id":2, + "name":"Morrow Phillips" + } + ], + "greeting":"Hello, Bertha Mack! You have 8 unread messages.", + "favoriteFruit":"banana" + }, + { + "_id":"5ce6dc0c6d8631fbfdd2afc7", + "index":2, + "guid":"66ca5411-4c88-4347-9972-e1016f628098", + "isActive":false, + "balance":"$2,732.22", + "picture":"http://placehold.it/32x32", + "age":33, + "eyeColor":"blue", + "name":"Fox Morgan", + "gender":"male", + "company":"PERKLE", + "email":"foxmorgan@perkle.com", + "phone":"+1 (985) 401-3450", + "address":"801 Whitty Lane, Snyderville, Guam, 5253", + "about":"Ex officia eu Lorem velit ullamco qui cupidatat irure sunt ea ad deserunt. Officia est consequat aute labore occaecat aliquip. Velit commodo cillum incididunt cupidatat ad id veniam aute labore tempor qui culpa voluptate dolor. Occaecat in ea id labore exercitation non tempor occaecat laboris aute irure fugiat dolor mollit. Voluptate non proident officia deserunt ex et ullamco aute eiusmod cupidatat consequat elit id.\r\n", + "registered":"2015-04-02T06:40:53 -01:00", + "latitude":-27.612441, + "longitude":-134.005929, + "tags":[ + "occaecat", + "amet", + "eu", + "dolore", + "ad", + "fugiat", + "quis" + ], + "friends":[ + { + "id":0, + "name":"Case Preston" + }, + { + "id":1, + "name":"Pollard Dawson" + }, + { + "id":2, + "name":"Frye Mann" + } + ], + "greeting":"Hello, Fox Morgan! You have 2 unread messages.", + "favoriteFruit":"apple" + }, + { + "_id":"5ce6dc0c0a7fea91e0a1fdf5", + "index":3, + "guid":"f895a236-fc0d-4c08-b2f0-9d1638dc256d", + "isActive":true, + "balance":"$2,746.32", + "picture":"http://placehold.it/32x32", + "age":34, + "eyeColor":"green", + "name":"Deleon Tucker", + "gender":"male", + "company":"ZANILLA", + "email":"deleontucker@zanilla.com", + "phone":"+1 (883) 415-2709", + "address":"540 Vandam Street, Chical, Wyoming, 5181", + "about":"Consectetur consectetur sint Lorem non id. Fugiat reprehenderit nulla dolore nisi culpa esse ea. Ad occaecat qui magna proident ex pariatur aliquip adipisicing do aute aute sunt. Aliqua aliqua et exercitation sunt ut adipisicing.\r\n", + "registered":"2017-10-08T09:05:49 -01:00", + "latitude":34.893845, + "longitude":110.699256, + "tags":[ + "culpa", + "sunt", + "sit", + "ut", + "eiusmod", + "laboris", + "ullamco" + ], + "friends":[ + { + "id":0, + "name":"Bernadine Pennington" + }, + { + "id":1, + "name":"Latoya Bradshaw" + }, + { + "id":2, + "name":"Iva Caldwell" + } + ], + "greeting":"Hello, Deleon Tucker! You have 7 unread messages.", + "favoriteFruit":"banana" + }, + { + "_id":"5ce6dc0c18bc92716a12a8e4", + "index":4, + "guid":"6ed45f42-1a2b-48b2-89ce-5fdb2505343b", + "isActive":true, + "balance":"$1,049.96", + "picture":"http://placehold.it/32x32", + "age":30, + "eyeColor":"blue", + "name":"Schwartz Norman", + "gender":"male", + "company":"UPDAT", + "email":"schwartznorman@updat.com", + "phone":"+1 (826) 404-3309", + "address":"925 Harman Street, Cornucopia, Georgia, 5748", + "about":"Qui Lorem ullamco veniam irure aliquip amet exercitation. Velit nisi id laboris adipisicing in esse adipisicing commodo cillum do exercitation tempor. Consequat tempor dolor minim consequat minim ad do tempor excepteur.\r\n", + "registered":"2014-08-10T08:34:27 -01:00", + "latitude":27.35547, + "longitude":-77.343791, + "tags":[ + "reprehenderit", + "nisi", + "duis", + "fugiat", + "id", + "non", + "laboris" + ], + "friends":[ + { + "id":0, + "name":"Dora Combs" + }, + { + "id":1, + "name":"Emerson Wade" + }, + { + "id":2, + "name":"Alma Mccormick" + } + ], + "greeting":"Hello, Schwartz Norman! You have 1 unread messages.", + "favoriteFruit":"apple" + }, + { + "_id":"5ce6dc0cb7ae44eb76c3e5fd", + "index":5, + "guid":"0516df27-73db-42a8-b2c3-d34bd976e031", + "isActive":false, + "balance":"$3,679.94", + "picture":"http://placehold.it/32x32", + "age":32, + "eyeColor":"brown", + "name":"Christi Oneal", + "gender":"female", + "company":"SUREPLEX", + "email":"christioneal@sureplex.com", + "phone":"+1 (985) 408-3098", + "address":"640 Fayette Street, Dennard, Washington, 7962", + "about":"Dolore fugiat sit non dolore nostrud mollit enim id sint culpa do reprehenderit ad. Velit occaecat incididunt nostrud aliqua incididunt do cillum occaecat laboris quis duis. Non tempor culpa aliquip est est consectetur ullamco elit. Voluptate et sit do et. Amet sit irure eu ex enim nulla anim deserunt ut. Sit aute ea ut fugiat eu tempor Lorem.\r\n", + "registered":"2015-05-10T09:24:56 -01:00", + "latitude":43.343805, + "longitude":79.535043, + "tags":[ + "occaecat", + "laboris", + "nulla", + "nisi", + "dolore", + "cillum", + "dolore" + ], + "friends":[ + { + "id":0, + "name":"Marquez Wiggins" + }, + { + "id":1, + "name":"Mai Fischer" + }, + { + "id":2, + "name":"Newman Davenport" + } + ], + "greeting":"Hello, Christi Oneal! You have 8 unread messages.", + "favoriteFruit":"strawberry" + } +]'); + + --Act + ut3.ut.expect( l_actual ).to_equal( l_actual ); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure long_json_diff as + l_expected json_element_t; + l_actual json_element_t; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_expected := json_element_t.parse('[ + { + "_id": "5ce6ec46cb9977b050f15d97", + "index": 0, + "guid": "1acb2b6b-15b5-4747-a62f-db477e18df61", + "isActive": false, + "balance": "$1,443.80", + "picture": "http://placehold.it/32x32", + "age": 33, + "eyeColor": "brown", + "name": "Carson Conley", + "gender": "male", + "company": "EYEWAX", + "email": "carsonconley@eyewax.com", + "phone": "+1 (873) 520-2117", + "address": "289 Wallabout Street, Cazadero, Nevada, 4802", + "about": "Lorem aliqua veniam eiusmod exercitation anim sunt esse qui tempor officia amet nulla labore enim. Fugiat eiusmod amet exercitation incididunt mollit pariatur amet et quis et ex amet adipisicing. Elit in commodo tempor adipisicing exercitation Lorem amet cillum sint sint aliquip. Officia enim do irure velit qui officia et reprehenderit qui enim.\r\n", + "registered": "2018-08-07T05:03:13 -01:00", + "latitude": -1.973252, + "longitude": 17.835529, + "tags": [ + "dolore", + "occaecat", + "proident", + "laborum", + "nostrud", + "non", + "occaecat" + ], + "friends": [ + { + "id": 0, + "name": "Riggs Cardenas" + }, + { + "id": 1, + "name": "Duncan Schultz" + }, + { + "id": 2, + "name": "Galloway Bond" + } + ], + "greeting": "Hello, Carson Conley! You have 5 unread messages.", + "favoriteFruit": "strawberry" + }, + { + "_id": "5ce6ec469ba57bef5c421021", + "index": 1, + "guid": "59be5b73-fffe-4a4f-acea-65c5abbdb53c", + "isActive": true, + "balance": "$3,895.35", + "picture": "http://placehold.it/32x32", + "age": 21, + "eyeColor": "brown", + "name": "Melton Carroll", + "gender": "male", + "company": "ISOSPHERE", + "email": "meltoncarroll@isosphere.com", + "phone": "+1 (804) 416-2235", + "address": "114 Windsor Place, Dubois, Oklahoma, 9648", + "about": "Pariatur ea voluptate aute dolor minim laborum cillum ad reprehenderit. Mollit sint voluptate duis et culpa amet irure laborum. Nulla veniam fugiat sint proident aliquip dolore laboris nisi et. Nisi in do aliqua voluptate cupidatat enim dolor minim minim qui tempor. Eu anim ea mollit sunt esse et est cillum cillum pariatur dolor. Ea anim duis sunt eiusmod sit cillum consectetur aliquip ad et elit culpa irure commodo.\r\n", + "registered": "2018-10-20T01:38:32 -01:00", + "latitude": 46.821539, + "longitude": 19.78817, + "tags": [ + "sunt", + "aliquip", + "commodo", + "occaecat", + "mollit", + "minim", + "sint" + ], + "friends": [ + { + "id": 0, + "name": "Tameka Reese" + }, + { + "id": 1, + "name": "Rosemarie Buckley" + }, + { + "id": 2, + "name": "Houston Moran" + } + ], + "greeting": "Hello, Melton Carroll! You have 10 unread messages.", + "favoriteFruit": "strawberry" + }, + { + "_id": "5ce6ec464e6f8751e75ed29f", + "index": 2, + "guid": "42e07b71-b769-4078-b226-f79048b75bd2", + "isActive": false, + "balance": "$3,366.81", + "picture": "http://placehold.it/32x32", + "age": 23, + "eyeColor": "blue", + "name": "Kathie Cameron", + "gender": "female", + "company": "EVENTIX", + "email": "kathiecameron@eventix.com", + "phone": "+1 (949) 416-3458", + "address": "171 Henderson Walk, Barstow, American Samoa, 3605", + "about": "Lorem est mollit consequat pariatur elit. Enim adipisicing ipsum sit labore exercitation fugiat qui eu enim. Quis irure Lorem exercitation laborum sunt quis Lorem pariatur officia veniam aute officia mollit quis.\r\n", + "registered": "2015-07-15T08:40:18 -01:00", + "latitude": -12.947501, + "longitude": 51.221756, + "tags": [ + "voluptate", + "officia", + "laborum", + "nulla", + "anim", + "mollit", + "adipisicing" + ], + "friends": [ + { + "id": 0, + "name": "Noelle Leonard" + }, + { + "id": 1, + "name": "Sally Barr" + }, + { + "id": 2, + "name": "Rosie Rutledge" + } + ], + "greeting": "Hello, Kathie Cameron! You have 10 unread messages.", + "favoriteFruit": "strawberry" + }, + { + "_id": "5ce6ec4632328a654d592cb6", + "index": 3, + "guid": "6b9124a9-fbde-4c60-8dac-e296f5daa3c4", + "isActive": true, + "balance": "$2,374.96", + "picture": "http://placehold.it/32x32", + "age": 32, + "eyeColor": "brown", + "name": "Ebony Carver", + "gender": "female", + "company": "EVENTEX", + "email": "ebonycarver@eventex.com", + "phone": "+1 (816) 535-3332", + "address": "452 Lott Street, Iberia, South Carolina, 1635", + "about": "Ea cupidatat occaecat in Lorem adipisicing quis sunt. Occaecat sit Lorem eiusmod et. Velit nostrud cupidatat do exercitation. Officia esse excepteur labore aliqua fugiat dolor duis. Ullamco qui ipsum eu do nostrud et laboris magna dolor cillum. Dolore eiusmod do occaecat dolore.\r\n", + "registered": "2017-04-12T09:20:02 -01:00", + "latitude": 65.70655, + "longitude": 150.667286, + "tags": [ + "do", + "laboris", + "exercitation", + "quis", + "laboris", + "amet", + "sint" + ], + "friends": [ + { + "id": 0, + "name": "Rowena Holloway" + }, + { + "id": 1, + "name": "Lee Chang" + }, + { + "id": 2, + "name": "Delaney Kennedy" + } + ], + "greeting": "Hello, Ebony Carver! You have 10 unread messages.", + "favoriteFruit": "apple" + }, + { + "_id": "5ce6ec46d9dbfbf9b184cee7", + "index": 4, + "guid": "9dece65b-6b48-4960-880b-7795ff63c81c", + "isActive": false, + "balance": "$2,927.54", + "picture": "http://placehold.it/32x32", + "age": 27, + "eyeColor": "green", + "name": "Mae Payne", + "gender": "female", + "company": "ZEPITOPE", + "email": "maepayne@zepitope.com", + "phone": "+1 (904) 531-2930", + "address": "575 Amity Street, Eden, Iowa, 4017", + "about": "Voluptate ex enim aliqua ea et proident ipsum est anim nostrud. Duis aliquip voluptate voluptate non aliquip. Elit commodo Lorem aliqua sit elit consectetur reprehenderit in aute minim. Dolor non incididunt do tempor aliquip esse non magna anim eiusmod ut id id.\r\n", + "registered": "2016-08-29T06:23:00 -01:00", + "latitude": -60.325313, + "longitude": 88.598722, + "tags": [ + "est", + "incididunt", + "officia", + "sunt", + "eu", + "ut", + "deserunt" + ], + "friends": [ + { + "id": 0, + "name": "Taylor Walton" + }, + { + "id": 1, + "name": "Celina Mcdonald" + }, + { + "id": 2, + "name": "Berry Rivers" + } + ], + "greeting": "Hello, Mae Payne! You have 4 unread messages.", + "favoriteFruit": "strawberry" + } +]'); + l_actual := json_element_t.parse('[ + { + "_id": "5ce6ec6660565269b16cf836", + "index": 0, + "guid": "c222eda5-d925-4163-89e3-4b0e50d5e297", + "isActive": false, + "balance": "$3,626.25", + "picture": "http://placehold.it/32x32", + "age": 28, + "eyeColor": "green", + "name": "Leigh Munoz", + "gender": "female", + "company": "OATFARM", + "email": "leighmunoz@oatfarm.com", + "phone": "+1 (969) 545-2708", + "address": "218 Mersereau Court, Homeworth, Connecticut, 4423", + "about": "Eiusmod exercitation incididunt ea incididunt anim voluptate. Duis laboris ut Lorem pariatur tempor voluptate occaecat laboris. Enim duis excepteur cillum ullamco pariatur sint. Dolor labore qui ullamco deserunt do consectetur labore velit occaecat officia incididunt Lorem dolore. Pariatur dolor voluptate ex adipisicing labore quis aliquip aliquip. Culpa tempor proident nisi occaecat aliqua mollit ullamco nisi cillum ipsum exercitation quis excepteur. Consequat officia ex ipsum id consequat deserunt sunt id nostrud magna.\r\n", + "registered": "2018-10-08T10:24:07 -01:00", + "latitude": -42.796797, + "longitude": -14.220273, + "tags": [ + "ex", + "elit", + "consectetur", + "ipsum", + "aute", + "ipsum", + "Lorem" + ], + "friends": [ + { + "id": 0, + "name": "Selena Dunn" + }, + { + "id": 1, + "name": "Wilda Haynes" + }, + { + "id": 2, + "name": "Calderon Long" + } + ], + "greeting": "Hello, Leigh Munoz! You have 6 unread messages.", + "favoriteFruit": "strawberry" + }, + { + "_id": "5ce6ec66383ddbf3c400e3ed", + "index": 1, + "guid": "2e778803-50d3-411f-b34d-47d0f19d03f7", + "isActive": false, + "balance": "$2,299.28", + "picture": "http://placehold.it/32x32", + "age": 23, + "eyeColor": "blue", + "name": "Velez Drake", + "gender": "male", + "company": "GENMY", + "email": "velezdrake@genmy.com", + "phone": "+1 (870) 564-2219", + "address": "526 Erskine Loop, Websterville, Nebraska, 1970", + "about": "Consectetur Lorem do ex est dolor. Consectetur do tempor amet elit. Amet dolore cupidatat Lorem sunt reprehenderit.\r\n", + "registered": "2017-11-24T04:42:37 -00:00", + "latitude": -45.78579, + "longitude": 142.062878, + "tags": [ + "do", + "esse", + "nisi", + "sunt", + "et", + "nisi", + "nostrud" + ], + "friends": [ + { + "id": 0, + "name": "Bessie Schmidt" + }, + { + "id": 1, + "name": "Harriett Lyons" + }, + { + "id": 2, + "name": "Jerry Gonzales" + } + ], + "greeting": "Hello, Velez Drake! You have 1 unread messages.", + "favoriteFruit": "apple" + }, + { + "_id": "5ce6ec660a8b5f95ed543305", + "index": 2, + "guid": "bb0eaa88-f7fd-4b72-8538-8c0b4595bcec", + "isActive": true, + "balance": "$3,085.28", + "picture": "http://placehold.it/32x32", + "age": 36, + "eyeColor": "green", + "name": "Gallegos Dominguez", + "gender": "male", + "company": "QOT", + "email": "gallegosdominguez@qot.com", + "phone": "+1 (947) 581-3675", + "address": "375 Temple Court, Beaulieu, Minnesota, 3880", + "about": "Qui consequat est aliquip esse minim Lorem qui quis. Enim consequat anim culpa consequat ex incididunt ad incididunt est id excepteur nulla culpa. Aliqua enim enim exercitation anim velit occaecat voluptate qui minim ut ullamco fugiat. Anim voluptate nulla minim labore dolore eu veniam. Exercitation sint eiusmod aute aliqua magna aliqua pariatur Lorem velit pariatur ex duis.\r\n", + "registered": "2019-03-11T12:36:55 -00:00", + "latitude": -1.619328, + "longitude": -160.580052, + "tags": [ + "ipsum", + "reprehenderit", + "id", + "aliqua", + "ad", + "do", + "sunt" + ], + "friends": [ + { + "id": 0, + "name": "Justice Bruce" + }, + { + "id": 1, + "name": "Alta Clements" + }, + { + "id": 2, + "name": "Amy Hobbs" + } + ], + "greeting": "Hello, Gallegos Dominguez! You have 10 unread messages.", + "favoriteFruit": "strawberry" + }, + { + "_id": "5ce6ec6600fb7aaee2d1243e", + "index": 3, + "guid": "4a4363b5-9d65-4b22-9b58-a5c8c1c5bd5d", + "isActive": false, + "balance": "$3,152.70", + "picture": "http://placehold.it/32x32", + "age": 37, + "eyeColor": "green", + "name": "Bobbie Baldwin", + "gender": "female", + "company": "IDEGO", + "email": "bobbiebaldwin@idego.com", + "phone": "+1 (937) 501-3123", + "address": "271 Coles Street, Deltaville, Massachusetts, 349", + "about": "Dolor labore quis Lorem eiusmod duis adipisicing ut. Aute aute aliquip exercitation eiusmod veniam ullamco irure sit est. Ut Lorem incididunt do sint laborum cillum Lorem commodo duis. Dolor nulla ad consectetur non cillum. Est excepteur esse mollit elit laborum ullamco exercitation sit esse. Reprehenderit occaecat ad ad reprehenderit adipisicing non Lorem ipsum fugiat culpa. Do quis non exercitation ea magna elit non.\r\n", + "registered": "2014-06-25T07:44:03 -01:00", + "latitude": -70.045195, + "longitude": 117.328462, + "tags": [ + "anim", + "excepteur", + "aliqua", + "mollit", + "non", + "in", + "adipisicing" + ], + "friends": [ + { + "id": 0, + "name": "Lora Little" + }, + { + "id": 1, + "name": "Stanton Pollard" + }, + { + "id": 2, + "name": "Bernice Knowles" + } + ], + "greeting": "Hello, Bobbie Baldwin! You have 5 unread messages.", + "favoriteFruit": "apple" + }, + { + "_id": "5ce6ec660585cbb589b34fc8", + "index": 4, + "guid": "18547241-6fd0-466d-9f79-21aeb0485294", + "isActive": false, + "balance": "$3,853.86", + "picture": "http://placehold.it/32x32", + "age": 32, + "eyeColor": "blue", + "name": "Erika Benton", + "gender": "female", + "company": "SURETECH", + "email": "erikabenton@suretech.com", + "phone": "+1 (833) 472-2277", + "address": "893 Jamison Lane, Grayhawk, Illinois, 1820", + "about": "Ullamco nisi quis esse fugiat eu proident nisi cupidatat reprehenderit nostrud nulla laborum duis. Duis quis ipsum ad voluptate enim. Et excepteur irure proident adipisicing enim eu veniam aliquip nostrud amet sit est. Non laborum reprehenderit qui ullamco occaecat elit sunt ea nostrud reprehenderit incididunt sunt.\r\n", + "registered": "2018-01-19T11:58:53 -00:00", + "latitude": -44.595301, + "longitude": 100.938225, + "tags": [ + "cupidatat", + "aliqua", + "nostrud", + "nostrud", + "ipsum", + "ipsum", + "commodo" + ], + "friends": [ + { + "id": 0, + "name": "Addie Benjamin" + }, + { + "id": 1, + "name": "Brock Nolan" + }, + { + "id": 2, + "name": "Betty Suarez" + } + ], + "greeting": "Hello, Erika Benton! You have 5 unread messages.", + "favoriteFruit": "apple" + }, + { + "_id": "5ce6ec66ff15753596332021", + "index": 5, + "guid": "f865dabb-4871-4f29-9c56-17361d254f39", + "isActive": true, + "balance": "$3,474.90", + "picture": "http://placehold.it/32x32", + "age": 32, + "eyeColor": "blue", + "name": "Rice Owens", + "gender": "male", + "company": "ACIUM", + "email": "riceowens@acium.com", + "phone": "+1 (975) 576-3718", + "address": "400 Halleck Street, Lafferty, District Of Columbia, 495", + "about": "Cupidatat laborum mollit non eu aute amet consectetur aliqua officia consectetur consequat. Tempor labore pariatur Lorem sint quis laborum est dolore et. Est ipsum incididunt eiusmod enim nostrud laboris duis est enim proident do laborum id culpa.\r\n", + "registered": "2018-05-06T02:43:06 -01:00", + "latitude": 2.843708, + "longitude": -3.301217, + "tags": [ + "laboris", + "velit", + "dolore", + "sunt", + "ad", + "aliqua", + "duis" + ], + "friends": [ + { + "id": 0, + "name": "Ramirez King" + }, + { + "id": 1, + "name": "Jeannie Boyer" + }, + { + "id": 2, + "name": "Deloris Jensen" + } + ], + "greeting": "Hello, Rice Owens! You have 9 unread messages.", + "favoriteFruit": "banana" + } +]'); + + --Act + ut3.ut.expect( l_actual ).to_equal( l_expected ); + --Assert + l_expected_message := q'[%Found: 133 differences, showing first 20 +%132 unequal values,1 missing properties +%Extra property 'object' on path :$[5] +%Actual value is '5ce6ec46cb9977b050f15d97' was expected to be '5ce6ec6660565269b16cf836' on path :$[0]._id +%Actual value is '5ce6ec469ba57bef5c421021' was expected to be '5ce6ec66383ddbf3c400e3ed' on path :$[1]._id +%Actual value is '5ce6ec4632328a654d592cb6' was expected to be '5ce6ec6600fb7aaee2d1243e' on path :$[3]._id +%Actual value is '5ce6ec464e6f8751e75ed29f' was expected to be '5ce6ec660a8b5f95ed543305' on path :$[2]._id +%Actual value is '5ce6ec46d9dbfbf9b184cee7' was expected to be '5ce6ec660585cbb589b34fc8' on path :$[4]._id +%Actual value is '59be5b73-fffe-4a4f-acea-65c5abbdb53c' was expected to be '2e778803-50d3-411f-b34d-47d0f19d03f7' on path :$[1].guid +%Actual value is '9dece65b-6b48-4960-880b-7795ff63c81c' was expected to be '18547241-6fd0-466d-9f79-21aeb0485294' on path :$[4].guid +%Actual value is '42e07b71-b769-4078-b226-f79048b75bd2' was expected to be 'bb0eaa88-f7fd-4b72-8538-8c0b4595bcec' on path :$[2].guid +%Actual value is '6b9124a9-fbde-4c60-8dac-e296f5daa3c4' was expected to be '4a4363b5-9d65-4b22-9b58-a5c8c1c5bd5d' on path :$[3].guid +%Actual value is '1acb2b6b-15b5-4747-a62f-db477e18df61' was expected to be 'c222eda5-d925-4163-89e3-4b0e50d5e297' on path :$[0].guid +%Actual value is 'false' was expected to be 'true' on path :$[2].isActive +%Actual value is 'true' was expected to be 'false' on path :$[3].isActive +%Actual value is 'true' was expected to be 'false' on path :$[1].isActive +%Actual value is '$3,895.35' was expected to be '$2,299.28' on path :$[1].balance +%Actual value is '$1,443.80' was expected to be '$3,626.25' on path :$[0].balance +%Actual value is '$3,366.81' was expected to be '$3,085.28' on path :$[2].balance +%Actual value is '$2,927.54' was expected to be '$3,853.86' on path :$[4].balance +%Actual value is '$2,374.96' was expected to be '$3,152.70' on path :$[3].balance +%Actual value is '23' was expected to be '36' on path :$[2].age%]'; + + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + end; / diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index a747638e8..814968524 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -57,6 +57,18 @@ create or replace package test_expectations_json is --%test( Two json use plsql function to extract diff pieces and compare) procedure to_diff_json_extract_diff; + + --%test( Long JSON test same ) + procedure long_json_test; + + --%test( JSON test same semantic content different order ) + procedure json_same_diffrent_ord; + + --%test( Long complex nested JSON test ) + procedure long_json_test2; + + --%test( Long complex json differences ) + procedure long_json_diff; end; / From c16de9750e60f57c6a19b37cebf9be4b4ea43076 Mon Sep 17 00:00:00 2001 From: lwasylow Date: Thu, 23 May 2019 20:41:10 +0100 Subject: [PATCH 08/24] Fixing sonar --- source/expectations/data_values/ut_compound_data_helper.pkb | 2 +- source/expectations/data_values/ut_json_tree_details.tpb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index 8e8148d42..f3d6737c7 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -690,7 +690,7 @@ create or replace package body ut_compound_data_helper is l_diffs(i).act_element_name,l_diffs(i).act_element_value,l_diffs(i).act_json_type, l_diffs(i).act_access_path, l_diffs(i).exp_element_name,l_diffs(i).exp_element_value,l_diffs(i).exp_json_type,l_diffs(i).exp_access_path); - return l_diffs.count; --TODO : na sqlrowcount + return l_diffs.count; end; function get_json_diffs_type(a_diffs_all tt_json_diff_tab) return tt_json_diff_type_tab is diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index ce213f4ea..748fe2395 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -69,7 +69,6 @@ create or replace type body ut_json_tree_details as member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_object_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as l_keys json_key_list; - l_object json_element_t; begin l_keys := coalesce(a_json_piece.get_keys,json_key_list()); From 45e91e127215d5574a30ae6e5d1ee08fc2086d7a Mon Sep 17 00:00:00 2001 From: lwasylow Date: Fri, 31 May 2019 07:11:19 +0100 Subject: [PATCH 09/24] Switch to use supertype --- .../data_values/ut_json_tree_details.tpb | 64 ++++++++++--------- .../data_values/ut_json_tree_details.tps | 12 ++-- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index 748fe2395..819228829 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -18,43 +18,46 @@ create or replace type body ut_json_tree_details as end; end; - member function get_json_value(a_json_piece json_object_t,a_key varchar2) return varchar2 is - l_json_el json_element_t := a_json_piece.get (a_key); + member function get_json_value(a_json_piece json_element_t,a_key varchar2) return varchar2 is + l_json json_object_t := treat(a_json_piece as json_object_t); + l_json_el json_element_t := l_json.get (a_key); l_val varchar2(4000); begin case - when l_json_el.is_string then l_val := a_json_piece.get_string(a_key); - when l_json_el.is_number then l_val := to_char(a_json_piece.get_number(a_key)); - when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); - when l_json_el.is_true then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); - when l_json_el.is_false then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); - when l_json_el.is_date then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR'); - when a_json_piece.is_timestamp then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); + when l_json_el.is_string then l_val := l_json.get_string(a_key); + when l_json_el.is_number then l_val := to_char(l_json.get_number(a_key)); + when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); + when l_json_el.is_true then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); + when l_json_el.is_false then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); + when l_json_el.is_date then l_val := to_char(l_json.get_date(a_key),'DD/MM/RRRR'); + when l_json.is_timestamp then l_val := to_char(l_json.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); else null; end case; return l_val; end; - member function get_json_value(a_json_piece json_array_t,a_key integer) return varchar2 is - l_json_el json_element_t := a_json_piece.get (a_key); + member function get_json_value(a_json_piece json_element_t,a_key integer) return varchar2 is + l_json json_array_t := treat(a_json_piece as json_array_t); + l_json_el json_element_t := treat(a_json_piece as json_array_t).get (a_key); l_val varchar2(4000); + begin case - when l_json_el.is_string then l_val := a_json_piece.get_string(a_key); - when l_json_el.is_number then l_val := to_char(a_json_piece.get_number(a_key)); - when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); - when l_json_el.is_true then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); - when l_json_el.is_false then l_val := ut_utils.boolean_to_char(a_json_piece.get_boolean(a_key)); - when l_json_el.is_date then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR'); - when a_json_piece.is_timestamp then l_val := to_char(a_json_piece.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); + when l_json_el.is_string then l_val := l_json.get_string(a_key); + when l_json_el.is_number then l_val := to_char(l_json.get_number(a_key)); + when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); + when l_json_el.is_true then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); + when l_json_el.is_false then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); + when l_json_el.is_date then l_val := to_char(l_json.get_date(a_key),'DD/MM/RRRR'); + when l_json_el.is_timestamp then l_val := to_char(l_json.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); else null; end case; return l_val; end; - member function get_json_size(a_json_piece json_object_t) return integer is + member function get_json_size(a_json_piece json_element_t) return integer is begin - return a_json_piece.get_size; + return treat(a_json_piece as json_object_t).get_size; end; member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, @@ -66,35 +69,36 @@ create or replace type body ut_json_tree_details as a_hierarchy_level, a_index_position,a_json_type, a_parent_type, a_array_element, a_parent_path); end; - member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_object_t, + member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as l_keys json_key_list; + l_object json_object_t := treat(a_json_piece as json_object_t); begin - l_keys := coalesce(a_json_piece.get_keys,json_key_list()); + l_keys := coalesce(l_object.get_keys,json_key_list()); for indx in 1 .. l_keys.count loop add_json_leaf(l_keys(indx), - get_json_value(a_json_piece,l_keys(indx)), + get_json_value(l_object,l_keys(indx)), a_parent_name, a_access_path||'.'||l_keys(indx), a_hierarchy_level, indx, - get_json_type(a_json_piece.get (l_keys(indx))), + get_json_type(l_object.get (l_keys(indx))), 'object', 0, a_access_path ); - case get_json_type(a_json_piece.get(l_keys(indx))) + case get_json_type(l_object.get(l_keys(indx))) when 'array' then traverse_array ( - treat (a_json_piece.get (l_keys(indx)) as json_array_t), + treat (l_object.get (l_keys(indx)) as json_array_t), l_keys(indx), a_hierarchy_level + 1, a_access_path||'.'||l_keys(indx) ); when 'object' then - traverse_object( treat (a_json_piece.get (l_keys(indx)) as json_object_t), + traverse_object( treat (l_object.get (l_keys(indx)) as json_object_t), l_keys (indx), a_hierarchy_level+1, a_access_path||'.'||l_keys(indx) @@ -104,13 +108,13 @@ create or replace type body ut_json_tree_details as end loop; end traverse_object; - member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_array_t, + member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as l_array json_array_t; l_type varchar2(50); l_name varchar2(4000); begin - l_array := a_json_piece; + l_array := treat(a_json_piece as json_array_t); for indx in 0 .. l_array.get_size - 1 loop l_type := get_json_type(l_array.get (indx)); @@ -137,7 +141,7 @@ create or replace type body ut_json_tree_details as ); when 'object' then traverse_object( - treat (a_json_piece.get (indx) as json_object_t), + treat (l_array.get (indx) as json_object_t), l_name, a_hierarchy_level+1, a_access_path||'['||indx||']' diff --git a/source/expectations/data_values/ut_json_tree_details.tps b/source/expectations/data_values/ut_json_tree_details.tps index e6f578b87..f829bd677 100644 --- a/source/expectations/data_values/ut_json_tree_details.tps +++ b/source/expectations/data_values/ut_json_tree_details.tps @@ -1,4 +1,4 @@ -create or replace type ut_json_tree_details as object ( +create or replace type ut_json_tree_details force as object ( /* utPLSQL - Version 3 Copyright 2016 - 2018 utPLSQL Project @@ -17,15 +17,15 @@ create or replace type ut_json_tree_details as object ( */ json_tree_info ut_json_leaf_tab, member function get_json_type(a_json_piece json_element_t) return varchar2, - member function get_json_value(a_json_piece json_object_t,a_key varchar2) return varchar2, - member function get_json_value(a_json_piece json_array_t,a_key integer) return varchar2, - member function get_json_size(a_json_piece json_object_t) return integer, + member function get_json_value(a_json_piece json_element_t,a_key varchar2) return varchar2, + member function get_json_value(a_json_piece json_element_t,a_key integer) return varchar2, + member function get_json_size(a_json_piece json_element_t) return integer, member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0,a_parent_path varchar2), - member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_object_t, + member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ), - member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_array_t, + member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ), member procedure init(self in out nocopy ut_json_tree_details,a_json_doc in json_element_t, a_level_in integer := 0), constructor function ut_json_tree_details( From ac8ab96d3396c325972991b0cb6c902f588b136c Mon Sep 17 00:00:00 2001 From: lwasylow Date: Fri, 31 May 2019 13:19:55 +0100 Subject: [PATCH 10/24] PHASE3 : Adding a functionality for install in pre 12.2 --- source/dummy/json_element_t.sql | 56 ++++++++++++++ .../data_values/ut_data_value_json.tpb | 6 ++ .../data_values/ut_json_tree_details.tpb | 73 +++++++++++++------ source/expectations/matchers/ut_equal.tpb | 4 + source/install.sql | 4 + .../expectations/test_expectations_json.pkb | 7 +- .../expectations/test_expectations_json.pks | 3 + 7 files changed, 131 insertions(+), 22 deletions(-) create mode 100644 source/dummy/json_element_t.sql diff --git a/source/dummy/json_element_t.sql b/source/dummy/json_element_t.sql new file mode 100644 index 000000000..c6ef7de16 --- /dev/null +++ b/source/dummy/json_element_t.sql @@ -0,0 +1,56 @@ +BEGIN + + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then + null; + $else + execute immediate 'create or replace type json_element_t as object ( + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + dummy_val number, + constructor function json_element_t(self in out nocopy json_element_t) return self as result +);'; + + execute immediate q'[create or replace type body json_element_t as + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + constructor function json_element_t(self in out nocopy json_element_t) return self as result is + begin + raise_application_error(ut_utils.gc_invalid_version_no,'This element is not supported pre 12.2 version, please upgrade.'); + return; + end; + +end;]'; + + $end + +END; +/ \ No newline at end of file diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb index 5c100791a..c3d9d3ebe 100644 --- a/source/expectations/data_values/ut_data_value_json.tpb +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -21,7 +21,9 @@ create or replace type body ut_data_value_json as constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result is begin self.is_data_null := case when a_value is null then 1 else 0 end; + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then self.data_value := case when a_value is null then null else a_value.to_clob end; + $end self.self_type := $$plsql_unit; self.data_type := 'json'; self.json_tree := ut_json_tree_details(a_value); @@ -140,7 +142,11 @@ create or replace type body ut_data_value_json as member function get_elements_count return integer is begin + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then return json_element_t.parse(self.data_value).get_size; + $else + return null; + $end end; member function get_json_count_info return varchar2 is diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index 819228829..630612731 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -2,6 +2,7 @@ create or replace type body ut_json_tree_details as member function get_json_type(a_json_piece json_element_t) return varchar2 is begin + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then return case when a_json_piece.is_object then 'object' when a_json_piece.is_array then 'array' @@ -15,49 +16,63 @@ create or replace type body ut_json_tree_details as when a_json_piece.is_timestamp then 'timestamp' when a_json_piece.is_scalar then 'scalar' else null - end; + end; + $else + return null; + $end end; member function get_json_value(a_json_piece json_element_t,a_key varchar2) return varchar2 is - l_json json_object_t := treat(a_json_piece as json_object_t); - l_json_el json_element_t := l_json.get (a_key); + l_json_el json_element_t; l_val varchar2(4000); begin + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then + l_json_el := treat(a_json_piece as json_object_t).get (a_key); case - when l_json_el.is_string then l_val := l_json.get_string(a_key); - when l_json_el.is_number then l_val := to_char(l_json.get_number(a_key)); - when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); - when l_json_el.is_true then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); - when l_json_el.is_false then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); - when l_json_el.is_date then l_val := to_char(l_json.get_date(a_key),'DD/MM/RRRR'); - when l_json.is_timestamp then l_val := to_char(l_json.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); + when l_json_el.is_string then l_val := treat(a_json_piece as json_object_t).get_string(a_key); + when l_json_el.is_number then l_val := to_char(treat(a_json_piece as json_object_t).get_number(a_key)); + when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_object_t).get_boolean(a_key)); + when l_json_el.is_true then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_object_t).get_boolean(a_key)); + when l_json_el.is_false then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_object_t).get_boolean(a_key)); + when l_json_el.is_date then l_val := to_char(treat(a_json_piece as json_object_t).get_date(a_key),'DD/MM/RRRR'); + when l_json_el.is_timestamp then l_val := to_char(treat(a_json_piece as json_object_t).get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); else null; end case; return l_val; + $else + return null; + $end end; member function get_json_value(a_json_piece json_element_t,a_key integer) return varchar2 is - l_json json_array_t := treat(a_json_piece as json_array_t); - l_json_el json_element_t := treat(a_json_piece as json_array_t).get (a_key); - l_val varchar2(4000); - + l_json_el json_element_t; + l_val varchar2(4000); begin + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then + l_json_el := treat(a_json_piece as json_array_t).get (a_key); case - when l_json_el.is_string then l_val := l_json.get_string(a_key); - when l_json_el.is_number then l_val := to_char(l_json.get_number(a_key)); - when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); - when l_json_el.is_true then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); - when l_json_el.is_false then l_val := ut_utils.boolean_to_char(l_json.get_boolean(a_key)); - when l_json_el.is_date then l_val := to_char(l_json.get_date(a_key),'DD/MM/RRRR'); - when l_json_el.is_timestamp then l_val := to_char(l_json.get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); + when l_json_el.is_string then l_val := treat(a_json_piece as json_array_t).get_string(a_key); + when l_json_el.is_number then l_val := to_char(treat(a_json_piece as json_array_t).get_number(a_key)); + when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_array_t).get_boolean(a_key)); + when l_json_el.is_true then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_array_t).get_boolean(a_key)); + when l_json_el.is_false then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_array_t).get_boolean(a_key)); + when l_json_el.is_date then l_val := to_char(treat(a_json_piece as json_array_t).get_date(a_key),'DD/MM/RRRR'); + when l_json_el.is_timestamp then l_val := to_char(treat(a_json_piece as json_array_t).get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); else null; end case; return l_val; + $else + return null; + $end end; member function get_json_size(a_json_piece json_element_t) return integer is begin + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then return treat(a_json_piece as json_object_t).get_size; + $else + return null; + $end end; member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, @@ -71,9 +86,12 @@ create or replace type body ut_json_tree_details as member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_keys json_key_list; l_object json_object_t := treat(a_json_piece as json_object_t); + $end begin + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_keys := coalesce(l_object.get_keys,json_key_list()); for indx in 1 .. l_keys.count @@ -106,14 +124,20 @@ create or replace type body ut_json_tree_details as else null; end case; end loop; + $else + null; + $end end traverse_object; member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_array json_array_t; + $end l_type varchar2(50); l_name varchar2(4000); begin + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_array := treat(a_json_piece as json_array_t); for indx in 0 .. l_array.get_size - 1 loop @@ -149,15 +173,22 @@ create or replace type body ut_json_tree_details as else null; end case; end loop; + $else + null; + $end end traverse_array; member procedure init(self in out nocopy ut_json_tree_details,a_json_doc in json_element_t, a_level_in integer := 0) is begin + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then if a_json_doc.is_object then traverse_object(treat (a_json_doc as json_object_t),null,1,'$'); elsif a_json_doc.is_array then traverse_array(treat (a_json_doc as json_array_t),null,1,'$'); end if; + $else + null; + $end end; constructor function ut_json_tree_details( diff --git a/source/expectations/matchers/ut_equal.tpb b/source/expectations/matchers/ut_equal.tpb index 2a8af6c91..2f164dfeb 100644 --- a/source/expectations/matchers/ut_equal.tpb +++ b/source/expectations/matchers/ut_equal.tpb @@ -249,10 +249,14 @@ create or replace type body ut_equal as || chr(10) || 'Diff:' || treat(expected as ut_data_value_refcursor).diff( a_actual, options ); elsif self.expected is of (ut_data_value_json) then + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_result := 'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info() || chr(10) || 'Diff:' || treat(expected as ut_data_value_json).diff( a_actual, options ); + $else + l_result := 'This version doesnt support json'; + $end else l_result := 'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info() diff --git a/source/install.sql b/source/install.sql index 2c7a6916d..3f307f6a7 100644 --- a/source/install.sql +++ b/source/install.sql @@ -34,6 +34,7 @@ alter session set current_schema = &&ut3_owner; @@check_sys_grants.sql --set define off + --dbms_output buffer cache table @@install_component.sql 'core/ut_dbms_output_cache.sql' @@ -56,6 +57,9 @@ alter session set current_schema = &&ut3_owner; @@install_component.sql 'reporters/ut_ansiconsole_helper.pks' @@install_component.sql 'reporters/ut_ansiconsole_helper.pkb' +--dummy objects +@@install_component.sql 'dummy/json_element_t.sql' + --event manager objects @@install_component.sql 'core/events/ut_event_item.tps' @@install_component.sql 'core/events/ut_event_listener.tps' diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index 3060203b3..bc236acc7 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -1,10 +1,13 @@ create or replace package body test_expectations_json is + procedure cleanup_expectations is begin ut3_tester_helper.main_helper.clear_expectations( ); end; + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then + procedure success_on_same_data as l_expected json_element_t; @@ -1584,6 +1587,8 @@ create or replace package body test_expectations_json is --Assert ut.expect(l_actual_message).to_be_like(l_expected_message); end; - + + $end + end; / diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index 814968524..f17bc919f 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -1,11 +1,13 @@ create or replace package test_expectations_json is + --%suite(json expectations) --%suitepath(utplsql.test_user.expectations) --%aftereach procedure cleanup_expectations; + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then --%test(Gives success for identical data) procedure success_on_same_data; @@ -70,5 +72,6 @@ create or replace package test_expectations_json is --%test( Long complex json differences ) procedure long_json_diff; + $end end; / From 587da958231018827dbb99481472166e5086b20a Mon Sep 17 00:00:00 2001 From: lwasylow Date: Fri, 31 May 2019 13:52:10 +0100 Subject: [PATCH 11/24] PHASE3 : Adding a functionality for install in pre 12.2 --- source/uninstall_objects.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/uninstall_objects.sql b/source/uninstall_objects.sql index 3d270ecea..ff501bf93 100644 --- a/source/uninstall_objects.sql +++ b/source/uninstall_objects.sql @@ -195,6 +195,10 @@ drop package ut_metadata; drop package ut_ansiconsole_helper; +$if dbms_db_version.version = 12 and dbms_db_version.release = 1 or dbms_db_version.version < 12 $then +drop type json_element_t cascade; +$end + drop package ut_utils; drop sequence ut_savepoint_seq; From 7192a516c47c6339700ec99a3592876db05443c0 Mon Sep 17 00:00:00 2001 From: LUKASZ104 Date: Fri, 31 May 2019 14:04:25 +0100 Subject: [PATCH 12/24] PHASE3 : Adding a functionality for install in pre 12.2 --- source/uninstall_objects.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/uninstall_objects.sql b/source/uninstall_objects.sql index ff501bf93..fa91a495f 100644 --- a/source/uninstall_objects.sql +++ b/source/uninstall_objects.sql @@ -196,7 +196,7 @@ drop package ut_metadata; drop package ut_ansiconsole_helper; $if dbms_db_version.version = 12 and dbms_db_version.release = 1 or dbms_db_version.version < 12 $then -drop type json_element_t cascade; +drop type json_element_t force; $end drop package ut_utils; From f4752588b464c6ccdb6db82c386dcf2a113fd0e0 Mon Sep 17 00:00:00 2001 From: LUKASZ104 Date: Fri, 31 May 2019 14:38:34 +0100 Subject: [PATCH 13/24] PHASE3 : Adding a functionality for install in pre 12.2 --- source/uninstall_objects.sql | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/source/uninstall_objects.sql b/source/uninstall_objects.sql index fa91a495f..b5b37c093 100644 --- a/source/uninstall_objects.sql +++ b/source/uninstall_objects.sql @@ -77,6 +77,8 @@ drop table ut_dbms_output_cache; drop type ut_expectation_compound force; +drop type ut_expectation_json force; + drop type ut_expectation force; drop package ut_expectation_processor; @@ -127,6 +129,8 @@ drop type ut_data_value_number force; drop type ut_data_value_refcursor force; +drop type ut_data_value_json force; + drop type ut_data_value_dsinterval force; drop type ut_data_value_date force; @@ -195,9 +199,14 @@ drop package ut_metadata; drop package ut_ansiconsole_helper; -$if dbms_db_version.version = 12 and dbms_db_version.release = 1 or dbms_db_version.version < 12 $then -drop type json_element_t force; -$end +begin + $if dbms_db_version.version = 12 and dbms_db_version.release = 1 or dbms_db_version.version < 12 $then + execute immediate 'drop type json_element_t force'; + $else + dbms_output.put_line('Nothing to drop'); + $end +end; +/ drop package ut_utils; From d48bdc2a324766efe3e828a7dc60748565405f47 Mon Sep 17 00:00:00 2001 From: LUKASZ104 Date: Fri, 31 May 2019 14:42:16 +0100 Subject: [PATCH 14/24] PHASE3 : Adding a functionality for install in pre 12.2 --- source/dummy/json_element_t.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/dummy/json_element_t.sql b/source/dummy/json_element_t.sql index c6ef7de16..dcb318fc3 100644 --- a/source/dummy/json_element_t.sql +++ b/source/dummy/json_element_t.sql @@ -1,7 +1,7 @@ BEGIN $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then - null; + dbms_output.put_line('Object exists , dont install'); $else execute immediate 'create or replace type json_element_t as object ( /* From 0aa94d31ca731d5858e55e91c8d0eb09543c9dcb Mon Sep 17 00:00:00 2001 From: lwasylow Date: Fri, 31 May 2019 21:10:52 +0100 Subject: [PATCH 15/24] PHASE3 : Adding a functionality for install in pre 12.2 --- source/expectations/matchers/ut_equal.tpb | 4 +--- test/ut3_user/expectations/test_expectations_json.pkb | 3 +-- test/ut3_user/expectations/test_expectations_json.pks | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/source/expectations/matchers/ut_equal.tpb b/source/expectations/matchers/ut_equal.tpb index 2f164dfeb..d138a50c5 100644 --- a/source/expectations/matchers/ut_equal.tpb +++ b/source/expectations/matchers/ut_equal.tpb @@ -248,14 +248,12 @@ create or replace type body ut_equal as 'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info() || chr(10) || 'Diff:' || treat(expected as ut_data_value_refcursor).diff( a_actual, options ); - elsif self.expected is of (ut_data_value_json) then $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then + elsif self.expected is of (ut_data_value_json) then l_result := 'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info() || chr(10) || 'Diff:' || treat(expected as ut_data_value_json).diff( a_actual, options ); - $else - l_result := 'This version doesnt support json'; $end else l_result := diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index bc236acc7..f7da7c516 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -1,13 +1,12 @@ create or replace package body test_expectations_json is + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then procedure cleanup_expectations is begin ut3_tester_helper.main_helper.clear_expectations( ); end; - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then - procedure success_on_same_data as l_expected json_element_t; diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index f17bc919f..640a3c5cc 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -1,5 +1,6 @@ create or replace package test_expectations_json is + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then --%suite(json expectations) --%suitepath(utplsql.test_user.expectations) @@ -7,8 +8,6 @@ create or replace package test_expectations_json is --%aftereach procedure cleanup_expectations; - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then - --%test(Gives success for identical data) procedure success_on_same_data; From a5c7ca035235730fb9b45658ef611b7edb8fefcc Mon Sep 17 00:00:00 2001 From: lwasylow Date: Fri, 7 Jun 2019 22:13:51 +0100 Subject: [PATCH 16/24] update tests --- test/install_ut3_user_tests.sql | 4 ++-- test/ut3_user/expectations/test_expectations_json.pkb | 4 ---- test/ut3_user/expectations/test_expectations_json.pks | 3 --- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/test/install_ut3_user_tests.sql b/test/install_ut3_user_tests.sql index f58c9df37..08bcff857 100644 --- a/test/install_ut3_user_tests.sql +++ b/test/install_ut3_user_tests.sql @@ -23,7 +23,7 @@ prompt Install user tests @@ut3_user/expectations/test_matchers.pks @@ut3_user/expectations/test_expectation_anydata.pks @@ut3_user/expectations/test_expectations_cursor.pks -@@ut3_user/expectations/test_expectations_json.pks +@@install_above_12_1.sql 'ut3_user/expectations/test_expectations_json.pks' @@ut3_user/api/test_ut_runner.pks @@ut3_user/api/test_ut_run.pks @@ut3_user/reporters.pks @@ -62,7 +62,7 @@ set define off @@ut3_user/expectations/test_matchers.pkb @@ut3_user/expectations/test_expectation_anydata.pkb @@ut3_user/expectations/test_expectations_cursor.pkb -@@ut3_user/expectations/test_expectations_json.pkb +@@install_above_12_1.sql 'ut3_user/expectations/test_expectations_json.pkb' @@ut3_user/api/test_ut_runner.pkb @@ut3_user/api/test_ut_run.pkb @@ut3_user/reporters.pkb diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index f7da7c516..bb69d6ff8 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -1,7 +1,5 @@ create or replace package body test_expectations_json is - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then - procedure cleanup_expectations is begin ut3_tester_helper.main_helper.clear_expectations( ); @@ -1587,7 +1585,5 @@ create or replace package body test_expectations_json is ut.expect(l_actual_message).to_be_like(l_expected_message); end; - $end - end; / diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index 640a3c5cc..ad48d9705 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -1,7 +1,5 @@ create or replace package test_expectations_json is - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then - --%suite(json expectations) --%suitepath(utplsql.test_user.expectations) @@ -71,6 +69,5 @@ create or replace package test_expectations_json is --%test( Long complex json differences ) procedure long_json_diff; - $end end; / From f6c95e519d1d42953a7785de0cf9bef5eec9c514 Mon Sep 17 00:00:00 2001 From: lwasylow Date: Fri, 7 Jun 2019 22:36:00 +0100 Subject: [PATCH 17/24] Update set define off --- test/install_ut3_user_tests.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/install_ut3_user_tests.sql b/test/install_ut3_user_tests.sql index 08bcff857..b7361e4aa 100644 --- a/test/install_ut3_user_tests.sql +++ b/test/install_ut3_user_tests.sql @@ -23,7 +23,9 @@ prompt Install user tests @@ut3_user/expectations/test_matchers.pks @@ut3_user/expectations/test_expectation_anydata.pks @@ut3_user/expectations/test_expectations_cursor.pks +set define on @@install_above_12_1.sql 'ut3_user/expectations/test_expectations_json.pks' +set define off @@ut3_user/api/test_ut_runner.pks @@ut3_user/api/test_ut_run.pks @@ut3_user/reporters.pks @@ -62,7 +64,9 @@ set define off @@ut3_user/expectations/test_matchers.pkb @@ut3_user/expectations/test_expectation_anydata.pkb @@ut3_user/expectations/test_expectations_cursor.pkb +set define on @@install_above_12_1.sql 'ut3_user/expectations/test_expectations_json.pkb' +set define off @@ut3_user/api/test_ut_runner.pkb @@ut3_user/api/test_ut_run.pkb @@ut3_user/reporters.pkb From e87aa6b24248d119d6110d7c8adb6faf782e831a Mon Sep 17 00:00:00 2001 From: lwasylow Date: Sat, 8 Jun 2019 09:59:20 +0100 Subject: [PATCH 18/24] Code cleanup --- .../expectations/data_values/ut_data_value_json.tpb | 13 ++++++++----- .../expectations/data_values/ut_data_value_json.tps | 3 ++- source/expectations/data_values/ut_json_leaf.tps | 2 +- .../data_values/ut_json_tree_details.tpb | 11 +---------- .../data_values/ut_json_tree_details.tps | 1 - 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb index c3d9d3ebe..ca5c8e631 100644 --- a/source/expectations/data_values/ut_data_value_json.tpb +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -16,10 +16,8 @@ create or replace type body ut_data_value_json as limitations under the License. */ - --IS JSON, JSON_EXISTS, JSON_TEXTCONTAINS - - constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result is - begin + member procedure init (self in out nocopy ut_data_value_json, a_value json_element_t) is + begin self.is_data_null := case when a_value is null then 1 else 0 end; $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then self.data_value := case when a_value is null then null else a_value.to_clob end; @@ -27,7 +25,12 @@ create or replace type body ut_data_value_json as self.self_type := $$plsql_unit; self.data_type := 'json'; self.json_tree := ut_json_tree_details(a_value); - self.data_id := sys_guid(); + self.data_id := sys_guid(); + end; + + constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result is + begin + init(a_value); return; end; diff --git a/source/expectations/data_values/ut_data_value_json.tps b/source/expectations/data_values/ut_data_value_json.tps index 30a30d3c7..a902e58fe 100644 --- a/source/expectations/data_values/ut_data_value_json.tps +++ b/source/expectations/data_values/ut_data_value_json.tps @@ -17,6 +17,7 @@ create or replace type ut_data_value_json under ut_compound_data_value( */ data_value clob, json_tree ut_json_tree_details, + member procedure init (self in out nocopy ut_data_value_json, a_value json_element_t), constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result, overriding member function is_null return boolean, overriding member function is_empty return boolean, @@ -28,4 +29,4 @@ create or replace type ut_data_value_json under ut_compound_data_value( member function get_json_count_info return varchar2, overriding member function get_object_info return varchar2 ) -/ + diff --git a/source/expectations/data_values/ut_json_leaf.tps b/source/expectations/data_values/ut_json_leaf.tps index 858b3c3c8..b283367a4 100644 --- a/source/expectations/data_values/ut_json_leaf.tps +++ b/source/expectations/data_values/ut_json_leaf.tps @@ -1,4 +1,4 @@ -create or replace type ut_json_leaf force authid current_user as object ( +create or replace type ut_json_leaf authid current_user as object ( /* utPLSQL - Version 3 Copyright 2016 - 2018 utPLSQL Project diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index 630612731..39c8d5d9d 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -65,16 +65,7 @@ create or replace type body ut_json_tree_details as return null; $end end; - - member function get_json_size(a_json_piece json_element_t) return integer is - begin - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then - return treat(a_json_piece as json_object_t).get_size; - $else - return null; - $end - end; - + member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0, a_parent_path varchar2) is diff --git a/source/expectations/data_values/ut_json_tree_details.tps b/source/expectations/data_values/ut_json_tree_details.tps index f829bd677..f2aa028f7 100644 --- a/source/expectations/data_values/ut_json_tree_details.tps +++ b/source/expectations/data_values/ut_json_tree_details.tps @@ -19,7 +19,6 @@ create or replace type ut_json_tree_details force as object ( member function get_json_type(a_json_piece json_element_t) return varchar2, member function get_json_value(a_json_piece json_element_t,a_key varchar2) return varchar2, member function get_json_value(a_json_piece json_element_t,a_key integer) return varchar2, - member function get_json_size(a_json_piece json_element_t) return integer, member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0,a_parent_path varchar2), From 273b21a4e434f0a87cce737eba6d0609cf3bcfdb Mon Sep 17 00:00:00 2001 From: lwasylow Date: Sat, 8 Jun 2019 10:09:07 +0100 Subject: [PATCH 19/24] fix slash --- source/expectations/data_values/ut_data_value_json.tps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/expectations/data_values/ut_data_value_json.tps b/source/expectations/data_values/ut_data_value_json.tps index a902e58fe..50f721bf6 100644 --- a/source/expectations/data_values/ut_data_value_json.tps +++ b/source/expectations/data_values/ut_data_value_json.tps @@ -29,4 +29,4 @@ create or replace type ut_data_value_json under ut_compound_data_value( member function get_json_count_info return varchar2, overriding member function get_object_info return varchar2 ) - +/ From 9f8cc717ceecfe8bfad6fb249cb718378f7b03c7 Mon Sep 17 00:00:00 2001 From: lwasylow Date: Sat, 8 Jun 2019 22:39:29 +0100 Subject: [PATCH 20/24] Creating a dummy specs and removing conditional compilation for code cleanup --- source/dummy/json_element_t.sql | 56 ----------- .../data_values/ut_data_value_json.tpb | 6 -- .../data_values/ut_json_tree_details.tpb | 28 ------ source/expectations/json_objects_specs.sql | 95 +++++++++++++++++++ source/install.sql | 5 +- 5 files changed, 97 insertions(+), 93 deletions(-) delete mode 100644 source/dummy/json_element_t.sql create mode 100644 source/expectations/json_objects_specs.sql diff --git a/source/dummy/json_element_t.sql b/source/dummy/json_element_t.sql deleted file mode 100644 index dcb318fc3..000000000 --- a/source/dummy/json_element_t.sql +++ /dev/null @@ -1,56 +0,0 @@ -BEGIN - - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then - dbms_output.put_line('Object exists , dont install'); - $else - execute immediate 'create or replace type json_element_t as object ( - /* - utPLSQL - Version 3 - Copyright 2016 - 2018 utPLSQL Project - - Licensed under the Apache License, Version 2.0 (the "License"): - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - dummy_val number, - constructor function json_element_t(self in out nocopy json_element_t) return self as result -);'; - - execute immediate q'[create or replace type body json_element_t as - /* - utPLSQL - Version 3 - Copyright 2016 - 2018 utPLSQL Project - - Licensed under the Apache License, Version 2.0 (the "License"): - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - - constructor function json_element_t(self in out nocopy json_element_t) return self as result is - begin - raise_application_error(ut_utils.gc_invalid_version_no,'This element is not supported pre 12.2 version, please upgrade.'); - return; - end; - -end;]'; - - $end - -END; -/ \ No newline at end of file diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb index ca5c8e631..1112f8793 100644 --- a/source/expectations/data_values/ut_data_value_json.tpb +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -19,9 +19,7 @@ create or replace type body ut_data_value_json as member procedure init (self in out nocopy ut_data_value_json, a_value json_element_t) is begin self.is_data_null := case when a_value is null then 1 else 0 end; - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then self.data_value := case when a_value is null then null else a_value.to_clob end; - $end self.self_type := $$plsql_unit; self.data_type := 'json'; self.json_tree := ut_json_tree_details(a_value); @@ -145,11 +143,7 @@ create or replace type body ut_data_value_json as member function get_elements_count return integer is begin - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then return json_element_t.parse(self.data_value).get_size; - $else - return null; - $end end; member function get_json_count_info return varchar2 is diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index 39c8d5d9d..e7b8c7318 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -2,7 +2,6 @@ create or replace type body ut_json_tree_details as member function get_json_type(a_json_piece json_element_t) return varchar2 is begin - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then return case when a_json_piece.is_object then 'object' when a_json_piece.is_array then 'array' @@ -17,16 +16,12 @@ create or replace type body ut_json_tree_details as when a_json_piece.is_scalar then 'scalar' else null end; - $else - return null; - $end end; member function get_json_value(a_json_piece json_element_t,a_key varchar2) return varchar2 is l_json_el json_element_t; l_val varchar2(4000); begin - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_json_el := treat(a_json_piece as json_object_t).get (a_key); case when l_json_el.is_string then l_val := treat(a_json_piece as json_object_t).get_string(a_key); @@ -39,16 +34,12 @@ create or replace type body ut_json_tree_details as else null; end case; return l_val; - $else - return null; - $end end; member function get_json_value(a_json_piece json_element_t,a_key integer) return varchar2 is l_json_el json_element_t; l_val varchar2(4000); begin - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_json_el := treat(a_json_piece as json_array_t).get (a_key); case when l_json_el.is_string then l_val := treat(a_json_piece as json_array_t).get_string(a_key); @@ -61,9 +52,6 @@ create or replace type body ut_json_tree_details as else null; end case; return l_val; - $else - return null; - $end end; member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, @@ -77,12 +65,9 @@ create or replace type body ut_json_tree_details as member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_keys json_key_list; l_object json_object_t := treat(a_json_piece as json_object_t); - $end begin - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_keys := coalesce(l_object.get_keys,json_key_list()); for indx in 1 .. l_keys.count @@ -115,20 +100,14 @@ create or replace type body ut_json_tree_details as else null; end case; end loop; - $else - null; - $end end traverse_object; member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_array json_array_t; - $end l_type varchar2(50); l_name varchar2(4000); begin - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then l_array := treat(a_json_piece as json_array_t); for indx in 0 .. l_array.get_size - 1 loop @@ -164,22 +143,15 @@ create or replace type body ut_json_tree_details as else null; end case; end loop; - $else - null; - $end end traverse_array; member procedure init(self in out nocopy ut_json_tree_details,a_json_doc in json_element_t, a_level_in integer := 0) is begin - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then if a_json_doc.is_object then traverse_object(treat (a_json_doc as json_object_t),null,1,'$'); elsif a_json_doc.is_array then traverse_array(treat (a_json_doc as json_array_t),null,1,'$'); end if; - $else - null; - $end end; constructor function ut_json_tree_details( diff --git a/source/expectations/json_objects_specs.sql b/source/expectations/json_objects_specs.sql new file mode 100644 index 000000000..3584e58f6 --- /dev/null +++ b/source/expectations/json_objects_specs.sql @@ -0,0 +1,95 @@ +BEGIN + + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then + dbms_output.put_line('Object exists , dont install'); + $else + dbms_output.put_line('Installing json structures specs.'); + execute immediate q'[create or replace TYPE JSON_Element_T FORCE AUTHID CURRENT_USER AS OBJECT( + dummyobjt NUMBER, + STATIC FUNCTION parse(jsn VARCHAR2) RETURN JSON_Element_T, + STATIC FUNCTION parse(jsn CLOB) RETURN JSON_Element_T, + STATIC FUNCTION parse(jsn BLOB) RETURN JSON_Element_T, + MEMBER FUNCTION to_Clob(self IN JSON_ELEMENT_T) RETURN CLOB, + MEMBER FUNCTION stringify(self IN JSON_ELEMENT_T) RETURN VARCHAR2, + MEMBER FUNCTION is_Object(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION is_Array(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION is_Scalar(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION is_String(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION is_Number(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION is_Boolean(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION is_True(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION is_False(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION is_Null(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION is_Date(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION is_Timestamp(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + + MEMBER FUNCTION get_Size(self IN JSON_ELEMENT_T) RETURN NUMBER +) NOT FINAL NOT INSTANTIABLE;]'; + + execute immediate q'[create or replace TYPE JSON_KEY_LIST FORCE AS VARRAY(32767) OF VARCHAR2(4000);]'; + + execute immediate q'[create or replace TYPE JSON_Array_T FORCE AUTHID CURRENT_USER + UNDER JSON_Element_T( + dummy NUMBER, + CONSTRUCTOR FUNCTION JSON_Array_T(self IN OUT JSON_ARRAY_T) + RETURN SELF AS RESULT, + + MEMBER FUNCTION get(self IN JSON_ARRAY_T, pos NUMBER) + RETURN JSON_Element_T, + MEMBER FUNCTION get_String(self IN JSON_ARRAY_T, pos NUMBER) + RETURN VARCHAR2, + MEMBER FUNCTION get_Number(self IN JSON_ARRAY_T, pos NUMBER) + RETURN NUMBER, + MEMBER FUNCTION get_Boolean(self IN JSON_ARRAY_T, pos NUMBER) + RETURN BOOLEAN, + MEMBER FUNCTION get_Date(self IN JSON_ARRAY_T, pos NUMBER) + RETURN DATE, + MEMBER FUNCTION get_Timestamp(self IN JSON_ARRAY_T, pos NUMBER) + RETURN TIMESTAMP, + MEMBER FUNCTION get_Clob(self IN JSON_ARRAY_T, pos NUMBER) RETURN CLOB, + MEMBER PROCEDURE get_Clob(self IN OUT NOCOPY JSON_ARRAY_T, pos NUMBER, + c IN OUT NOCOPY CLOB), + MEMBER FUNCTION get_Blob(self IN JSON_ARRAY_T, pos NUMBER) RETURN BLOB, + MEMBER PROCEDURE get_Blob(self IN OUT NOCOPY JSON_ARRAY_T, pos NUMBER, + b IN OUT NOCOPY BLOB), + MEMBER FUNCTION get_Type(self IN JSON_ARRAY_T, pos NUMBER) + RETURN VARCHAR2 +) FINAL;]'; + + execute immediate q'[create or replace TYPE JSON_Object_T AUTHID CURRENT_USER UNDER JSON_Element_T( + dummy NUMBER, + CONSTRUCTOR FUNCTION JSON_Object_T(self IN OUT JSON_OBJECT_T) + RETURN SELF AS RESULT, + + MEMBER FUNCTION get(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN JSON_Element_T, + MEMBER FUNCTION get_Object(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN JSON_OBJECT_T, + MEMBER FUNCTION get_Array(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN JSON_ARRAY_T, + MEMBER FUNCTION get_String(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN VARCHAR2, + MEMBER FUNCTION get_Number(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN NUMBER, + MEMBER FUNCTION get_Boolean(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN BOOLEAN, + MEMBER FUNCTION get_Date(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN DATE, + MEMBER FUNCTION get_Timestamp(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN TIMESTAMP, + MEMBER FUNCTION get_Clob(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN CLOB, + MEMBER PROCEDURE get_Clob(self IN OUT NOCOPY JSON_OBJECT_T, + key VARCHAR2, c IN OUT NOCOPY CLOB), + MEMBER FUNCTION get_Blob(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN BLOB, + MEMBER PROCEDURE get_Blob(self IN OUT NOCOPY JSON_OBJECT_T, + key VARCHAR2, b IN OUT NOCOPY BLOB), + MEMBER FUNCTION get_Type(self IN JSON_OBJECT_T, key VARCHAR2) + RETURN VARCHAR2, + MEMBER FUNCTION get_Keys(self IN JSON_OBJECT_T) RETURN JSON_KEY_LIST +) FINAL;]'; + $end + +END; +/ \ No newline at end of file diff --git a/source/install.sql b/source/install.sql index 744b16c37..d3ba1ac69 100644 --- a/source/install.sql +++ b/source/install.sql @@ -57,9 +57,6 @@ alter session set current_schema = &&ut3_owner; @@install_component.sql 'reporters/ut_ansiconsole_helper.pks' @@install_component.sql 'reporters/ut_ansiconsole_helper.pkb' ---dummy objects -@@install_component.sql 'dummy/json_element_t.sql' - --event manager objects @@install_component.sql 'core/events/ut_event_item.tps' @@install_component.sql 'core/events/ut_event_listener.tps' @@ -88,6 +85,8 @@ alter session set current_schema = &&ut3_owner; @@install_component.sql 'core/types/ut_reporter_base.tps' @@install_component.sql 'core/types/ut_reporters.tps' + +@@install_component.sql 'expectations/json_objects_specs.sql' @@install_component.sql 'expectations/matchers/ut_matcher_options_items.tps' @@install_component.sql 'expectations/matchers/ut_matcher_options.tps' @@install_component.sql 'expectations/data_values/ut_data_value.tps' From 9031856ac85a6a70e1f44b52822ba51b4394f0dd Mon Sep 17 00:00:00 2001 From: lwasylow Date: Thu, 13 Jun 2019 07:21:53 +0100 Subject: [PATCH 21/24] PR fixes. --- docs/userguide/expectations.md | 29 ++++++++++------- source/core/ut_utils.pkb | 6 +--- source/core/ut_utils.pks | 2 -- .../data_values/ut_compound_data_helper.pkb | 4 +-- .../data_values/ut_json_data_diff_tmp.sql | 4 +-- .../data_values/ut_json_tree_details.tpb | 12 +++---- source/expectations/matchers/ut_equal.tpb | 23 +++++--------- .../expectations/test_expectations_json.pkb | 31 ++++++++++++++++--- .../expectations/test_expectations_json.pks | 6 ++++ 9 files changed, 68 insertions(+), 49 deletions(-) diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index b1352f994..deda5ccf3 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -1301,18 +1301,21 @@ create or replace package test_expectations_json is --%suite(json expectations) --%test(Gives success for identical pieces of two different jsons) - procedure to_diff_json_extract_same + procedure to_diff_json_extract_same; + end; / create or replace package body test_expectations_json is - procedure to_diff_json_extract_same - as - l_expected json_object_t; - l_actual json_object_t; - BEGIN + + procedure to_diff_json_extract_same as + l_expected json_object_t; + l_actual json_object_t; + l_array_actual json_array_t; + l_array_expected json_array_t; + begin -- Arrange - l_expected := json_object_t.parse(' { + l_expected := json_object_t.parse(' { "Actors": [ { "name": "Tom Cruise", @@ -1348,8 +1351,9 @@ create or replace package body test_expectations_json is } ] }' - ); - l_actual := json_object_t.parse(' { + ); + + l_actual := json_object_t.parse(' { "Actors": { "name": "Krzystof Jarzyna", @@ -1365,11 +1369,12 @@ create or replace package body test_expectations_json is ] } }' - ); - + ); + l_array_actual := json_array_t(json_query(l_actual.stringify,'$.Actors.children')); + l_array_expected := json_array_t(json_query(l_expected.stringify,'$.Actors[1].children')); --Act - ut3.ut.expect(json_array_t(json_query(l_actual.stringify,'$.Actors.children'))).to_equal(json_array_t(json_query(l_expected.stringify,'$.Actors[1].children'))); + ut3.ut.expect(l_array_actual).to_equal(l_array_expected); end; end; diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 00f720df6..a27d658a7 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -199,11 +199,6 @@ create or replace package body ut_utils is return case a_value when 1 then true when 0 then false end; end; - function boolean_to_char(a_value boolean) return varchar2 is - begin - return case a_value when true then 'true' when false then 'false' end; - end; - function string_to_table(a_string varchar2, a_delimiter varchar2:= chr(10), a_skip_leading_delimiter varchar2 := 'N') return ut_varchar2_list is l_offset integer := 1; l_delimiter_position integer; @@ -495,6 +490,7 @@ create or replace package body ut_utils is begin execute immediate 'delete from ut_compound_data_tmp'; execute immediate 'delete from ut_compound_data_diff_tmp'; + execute immediate 'delete from ut_json_data_diff_tmp'; end; function to_version(a_version_no varchar2) return t_version is diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 3d8ddb764..b9c7a42c7 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -210,8 +210,6 @@ create or replace package ut_utils authid definer is function int_to_boolean(a_value integer) return boolean; - function boolean_to_char(a_value boolean) return varchar2; - /** * * Splits a given string into table of string by delimiter. diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index cfbd36d01..5a48708dd 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -698,8 +698,8 @@ create or replace package body ut_compound_data_helper is exp_element_name,exp_element_value,exp_json_type,exp_access_path) values (a_diff_id,l_diffs(i).difference_type, - l_diffs(i).act_element_name,l_diffs(i).act_element_value,l_diffs(i).act_json_type, l_diffs(i).act_access_path, - l_diffs(i).exp_element_name,l_diffs(i).exp_element_value,l_diffs(i).exp_json_type,l_diffs(i).exp_access_path); + l_diffs(i).act_element_name,ut_utils.to_string(l_diffs(i).act_element_value,null),l_diffs(i).act_json_type, l_diffs(i).act_access_path, + l_diffs(i).exp_element_name,ut_utils.to_string(l_diffs(i).exp_element_value,null),l_diffs(i).exp_json_type,l_diffs(i).exp_access_path); return l_diffs.count; end; diff --git a/source/expectations/data_values/ut_json_data_diff_tmp.sql b/source/expectations/data_values/ut_json_data_diff_tmp.sql index 73966ce0c..feb6d44c6 100644 --- a/source/expectations/data_values/ut_json_data_diff_tmp.sql +++ b/source/expectations/data_values/ut_json_data_diff_tmp.sql @@ -15,11 +15,11 @@ create global temporary table ut_json_data_diff_tmp( diff_id raw(128), difference_type varchar2(250), act_element_name varchar2(2000), - act_element_value clob, + act_element_value varchar2(4000), act_json_type varchar2(100), act_access_path varchar2(4000), exp_element_name varchar2(2000), - exp_element_value clob, + exp_element_value varchar2(4000), exp_json_type varchar2(2000), exp_access_path varchar2(4000) ) on commit preserve rows; diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index e7b8c7318..d295ea147 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -26,9 +26,9 @@ create or replace type body ut_json_tree_details as case when l_json_el.is_string then l_val := treat(a_json_piece as json_object_t).get_string(a_key); when l_json_el.is_number then l_val := to_char(treat(a_json_piece as json_object_t).get_number(a_key)); - when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_object_t).get_boolean(a_key)); - when l_json_el.is_true then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_object_t).get_boolean(a_key)); - when l_json_el.is_false then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_object_t).get_boolean(a_key)); + when l_json_el.is_boolean then l_val := ut_utils.to_string(treat(a_json_piece as json_object_t).get_boolean(a_key)); + when l_json_el.is_true then l_val := ut_utils.to_string(treat(a_json_piece as json_object_t).get_boolean(a_key)); + when l_json_el.is_false then l_val := ut_utils.to_string(treat(a_json_piece as json_object_t).get_boolean(a_key)); when l_json_el.is_date then l_val := to_char(treat(a_json_piece as json_object_t).get_date(a_key),'DD/MM/RRRR'); when l_json_el.is_timestamp then l_val := to_char(treat(a_json_piece as json_object_t).get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); else null; @@ -44,9 +44,9 @@ create or replace type body ut_json_tree_details as case when l_json_el.is_string then l_val := treat(a_json_piece as json_array_t).get_string(a_key); when l_json_el.is_number then l_val := to_char(treat(a_json_piece as json_array_t).get_number(a_key)); - when l_json_el.is_boolean then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_array_t).get_boolean(a_key)); - when l_json_el.is_true then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_array_t).get_boolean(a_key)); - when l_json_el.is_false then l_val := ut_utils.boolean_to_char(treat(a_json_piece as json_array_t).get_boolean(a_key)); + when l_json_el.is_boolean then l_val := ut_utils.to_string(treat(a_json_piece as json_array_t).get_boolean(a_key)); + when l_json_el.is_true then l_val := ut_utils.to_string(treat(a_json_piece as json_array_t).get_boolean(a_key)); + when l_json_el.is_false then l_val := ut_utils.to_string(treat(a_json_piece as json_array_t).get_boolean(a_key)); when l_json_el.is_date then l_val := to_char(treat(a_json_piece as json_array_t).get_date(a_key),'DD/MM/RRRR'); when l_json_el.is_timestamp then l_val := to_char(treat(a_json_piece as json_array_t).get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); else null; diff --git a/source/expectations/matchers/ut_equal.tpb b/source/expectations/matchers/ut_equal.tpb index d138a50c5..9df566af7 100644 --- a/source/expectations/matchers/ut_equal.tpb +++ b/source/expectations/matchers/ut_equal.tpb @@ -243,24 +243,17 @@ create or replace type body ut_equal as l_result varchar2(32767); begin if self.expected.data_type = a_actual.data_type and self.expected.is_diffable then - if self.expected is of (ut_data_value_refcursor) then l_result := 'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info() - || chr(10) || 'Diff:' || - treat(expected as ut_data_value_refcursor).diff( a_actual, options ); - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then - elsif self.expected is of (ut_data_value_json) then - l_result := - 'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info() - || chr(10) || 'Diff:' || - treat(expected as ut_data_value_json).diff( a_actual, options ); - $end + || chr(10) || 'Diff:' || + case + when self.expected is of (ut_data_value_refcursor) then + treat(expected as ut_data_value_refcursor).diff( a_actual, options ) + when self.expected is of (ut_data_value_json) then + treat(expected as ut_data_value_json).diff( a_actual, options ) else - l_result := - 'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info() - || chr(10) || 'Diff:' || - expected.diff( a_actual, options ); - end if; + expected.diff( a_actual, options ) + end; else l_result := (self as ut_matcher).failure_message(a_actual) || ': '|| self.expected.to_string_report(); end if; diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index bb69d6ff8..e42925196 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -88,7 +88,7 @@ create or replace package body test_expectations_json is %Actual type is 'string' was expected to be 'boolean' on path :$.Aidan Gillen.aboolean %Actual value is 'True Blood' was expected to be 'Big Love' on path :$.Annie Fitzgerald[0] %Actual value is 'Big Love' was expected to be 'True Blood' on path :$.Annie Fitzgerald[1] -%Actual value is 'false' was expected to be 'true' on path :$.Aidan Gillen.boolean +%Actual value is 'FALSE' was expected to be 'TRUE' on path :$.Aidan Gillen.boolean %Actual value is 'Game of Thrones' was expected to be 'Game of Thron"es' on path :$.Aidan Gillen.array[0]%]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert @@ -1048,7 +1048,7 @@ create or replace package body test_expectations_json is end; procedure long_json_diff as - l_expected json_element_t; + l_expected json_element_t; l_actual json_element_t; l_expected_message varchar2(32767); l_actual_message varchar2(32767); @@ -1570,9 +1570,9 @@ create or replace package body test_expectations_json is %Actual value is '42e07b71-b769-4078-b226-f79048b75bd2' was expected to be 'bb0eaa88-f7fd-4b72-8538-8c0b4595bcec' on path :$[2].guid %Actual value is '6b9124a9-fbde-4c60-8dac-e296f5daa3c4' was expected to be '4a4363b5-9d65-4b22-9b58-a5c8c1c5bd5d' on path :$[3].guid %Actual value is '1acb2b6b-15b5-4747-a62f-db477e18df61' was expected to be 'c222eda5-d925-4163-89e3-4b0e50d5e297' on path :$[0].guid -%Actual value is 'false' was expected to be 'true' on path :$[2].isActive -%Actual value is 'true' was expected to be 'false' on path :$[3].isActive -%Actual value is 'true' was expected to be 'false' on path :$[1].isActive +%Actual value is 'FALSE' was expected to be 'TRUE' on path :$[2].isActive +%Actual value is 'TRUE' was expected to be 'FALSE' on path :$[3].isActive +%Actual value is 'TRUE' was expected to be 'FALSE' on path :$[1].isActive %Actual value is '$3,895.35' was expected to be '$2,299.28' on path :$[1].balance %Actual value is '$1,443.80' was expected to be '$3,626.25' on path :$[0].balance %Actual value is '$3,366.81' was expected to be '$3,085.28' on path :$[2].balance @@ -1585,5 +1585,26 @@ create or replace package body test_expectations_json is ut.expect(l_actual_message).to_be_like(l_expected_message); end; + procedure check_json_objects is + l_expected json_object_t; + l_actual json_object_t; + begin + l_expected := json_object_t('{ "name" : "Bond", "proffesion" : "spy", "drink" : "martini"}'); + l_actual := json_object_t('{ "proffesion" : "spy","name" : "Bond", "drink" : "martini"}'); + ut3.ut.expect( l_actual ).to_equal( l_expected ); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure check_json_arrays is + l_expected json_array_t; + l_actual json_array_t; + begin + l_expected := json_array_t('[ {"name" : "Bond", "proffesion" : "spy", "drink" : "martini"} , {"name" : "Kloss", "proffesion" : "spy", "drink" : "beer"} ]'); + l_actual := json_array_t('[ {"name" : "Bond", "proffesion" : "spy", "drink" : "martini"} , {"name" : "Kloss", "proffesion" : "spy", "drink" : "beer"} ]'); + ut3.ut.expect( l_actual ).to_equal( l_expected ); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + end; / diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index ad48d9705..64dff42e8 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -68,6 +68,12 @@ create or replace package test_expectations_json is --%test( Long complex json differences ) procedure long_json_diff; + + --%test( Compare two objects json ) + procedure check_json_objects; + + --%test( Compare two json arrays ) + procedure check_json_arrays; end; / From ee73b693f44a690cd528e567d66ea743a072f86d Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sun, 16 Jun 2019 00:40:29 +0100 Subject: [PATCH 22/24] Reverting development script changes. --- development/cleanup.sh | 2 +- development/install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/development/cleanup.sh b/development/cleanup.sh index 8cee4570e..313e382ca 100755 --- a/development/cleanup.sh +++ b/development/cleanup.sh @@ -3,7 +3,7 @@ #goto git root directory git rev-parse && cd "$(git rev-parse --show-cdup)" -#. development/env.sh +. development/env.sh "${SQLCLI}" sys/${ORACLE_PWD}@//${CONNECTION_STR} AS SYSDBA <<-SQL set echo on diff --git a/development/install.sh b/development/install.sh index 86d7e4da2..4104e2672 100755 --- a/development/install.sh +++ b/development/install.sh @@ -3,7 +3,7 @@ #goto git root directory git rev-parse && cd "$(git rev-parse --show-cdup)" -#. development/env.sh +. development/env.sh header="******************************************************************************************" if ! development/cleanup.sh; then From d6a6801cc92f0b29c51bdb3997939d41ef9ce736 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Mon, 17 Jun 2019 16:49:45 +0100 Subject: [PATCH 23/24] Refactored JSON code. Changed formatting of JSON-diff output. Changed date formats. --- source/core/ut_suite_manager.pkb | 2 +- .../data_values/ut_compound_data_helper.pkb | 133 ++++++---- .../data_values/ut_compound_data_helper.pks | 4 +- .../data_values/ut_data_value_json.tpb | 54 ++-- .../data_values/ut_json_data_diff_tmp.sql | 18 +- .../data_values/ut_json_tree_details.tpb | 246 ++++++++++-------- .../data_values/ut_json_tree_details.tps | 50 ++-- .../expectations/test_expectations_json.pkb | 93 ++++--- 8 files changed, 345 insertions(+), 255 deletions(-) diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index 87121918c..21e070865 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -916,7 +916,7 @@ create or replace package body ut_suite_manager is where exists ( select 1 from ]'||l_ut_owner||q'[.ut_suite_cache c - where 1 = 1 ]'||case when can_skip_all_objects_scan(l_owner_name) then q'[ + where 1 = 1 ]'||case when not can_skip_all_objects_scan(l_owner_name) then q'[ and exists ( select 1 from all_objects a diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index 5a48708dd..2a2ce9d9e 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -639,67 +639,87 @@ create or replace package body ut_compound_data_helper is l_result_diff tt_json_diff_tab := tt_json_diff_tab(); begin - with differences as ( - select - case - when (a.element_name is null or e.element_name is null) then gc_json_missing - when a.json_type != e.json_type then gc_json_type - when (decode(a.element_value,e.element_value,1,0) = 0) then gc_json_notequal - else gc_json_unknown end as difference_type, - case - when (a.element_name is null or e.element_name is null) then 1 - when a.json_type != e.json_type then 2 - when (decode(a.element_value,e.element_value,1,0) = 0) then 3 - else 4 end as order_by_type, - a.element_name as act_element_name, a.element_value as act_element_value, a.hierarchy_level as act_hierarchy_level, - a.index_position as act_index_position, a.json_type as act_json_type, a.access_path as act_access_path, - a.parent_name act_par_name, - e.element_name as exp_element_name, e.element_value as exp_element_value, e.hierarchy_level as exp_hierarchy_level, - e.index_position as exp_index_position, e.json_type as exp_json_type, e.access_path as exp_access_path, - e.parent_name exp_par_name - from table(a_act_json_data) a - full outer join table(a_exp_json_data) e - on decode(a.parent_name,e.parent_name,1,0)= 1 - and decode(a.parent_path,e.parent_path,1,0)= 1 - and ( - case when a.parent_type = 'object' or e.parent_type = 'object' then - decode(a.element_name,e.element_name,1,0) - else 1 end = 1 - ) - and ( - case when a.parent_type = 'array' or e.parent_type = 'array' then - decode(a.index_position,e.index_position,1,0) - else 1 end = 1 - ) - and a.hierarchy_level = e.hierarchy_level - where (a.element_name is null or e.element_name is null) - or (a.json_type != e.json_type) - or (decode(a.element_value,e.element_value,1,0) = 0) + with + differences as ( + select case + when (a.element_name is null or e.element_name is null) then gc_json_missing + when a.json_type != e.json_type then gc_json_type + when (decode(a.element_value,e.element_value,1,0) = 0) then gc_json_notequal + else gc_json_unknown + end as difference_type, + case + when (a.element_name is null or e.element_name is null) then 1 + when a.json_type != e.json_type then 2 + when (decode(a.element_value,e.element_value,1,0) = 0) then 3 + else 4 + end as order_by_type, + a.element_name as act_element_name, + a.element_value as act_element_value, + a.hierarchy_level as act_hierarchy_level, + a.index_position as act_index_position, + a.json_type as act_json_type, + a.access_path as act_access_path, + a.parent_name as act_par_name, + a.parent_path as act_parent_path, + e.element_name as exp_element_name, + e.element_value as exp_element_value, + e.hierarchy_level as exp_hierarchy_level, + e.index_position as exp_index_position, + e.json_type as exp_json_type, + e.access_path as exp_access_path, + e.parent_name as exp_par_name, + e.parent_path as exp_parent_path + from table(a_act_json_data) a + full outer join table(a_exp_json_data) e + on decode(a.parent_name,e.parent_name,1,0)= 1 + and decode(a.parent_path,e.parent_path,1,0)= 1 + and ( + case when a.parent_type = 'object' or e.parent_type = 'object' then + decode(a.element_name,e.element_name,1,0) + else 1 end = 1 + ) + and ( + case when a.parent_type = 'array' or e.parent_type = 'array' then + decode(a.index_position,e.index_position,1,0) + else 1 end = 1 + ) + and a.hierarchy_level = e.hierarchy_level + where (a.element_name is null or e.element_name is null) + or (a.json_type != e.json_type) + or (decode(a.element_value,e.element_value,1,0) = 0) ) - select difference_type, act_element_name, act_element_value, act_json_type, act_access_path, - exp_element_name, exp_element_value, exp_json_type, exp_access_path + select difference_type, + act_element_name, act_element_value, act_json_type, act_access_path, act_parent_path, + exp_element_name, exp_element_value, exp_json_type, exp_access_path, exp_parent_path bulk collect into l_result_diff from differences a - where not exists ( select 1 from differences b where (a.act_par_name = b.act_element_name and a.act_hierarchy_level - 1 = b.act_hierarchy_level) - or (a.exp_par_name = b.exp_element_name and a.exp_hierarchy_level - 1 = b.exp_hierarchy_level) - and a.difference_type = gc_json_missing and b.difference_type = gc_json_missing) - order by order_by_type, - nvl(act_hierarchy_level,exp_hierarchy_level),nvl(act_index_position,exp_index_position) nulls first, - nvl(act_element_name,exp_element_name) ; + where not exists ( + select 1 from differences b + where (a.act_par_name = b.act_element_name and a.act_hierarchy_level - 1 = b.act_hierarchy_level) + or (a.exp_par_name = b.exp_element_name and a.exp_hierarchy_level - 1 = b.exp_hierarchy_level) + and a.difference_type = gc_json_missing and b.difference_type = gc_json_missing + ) + order by order_by_type, + nvl(act_hierarchy_level,exp_hierarchy_level), + nvl(act_index_position,exp_index_position) nulls first, + nvl(act_element_name,exp_element_name) ; return l_result_diff; end; - function insert_json_diffs(a_diff_id raw, a_act_json_data ut_json_leaf_tab,a_exp_json_data ut_json_leaf_tab) return integer is + function insert_json_diffs(a_diff_id raw, a_act_json_data ut_json_leaf_tab, a_exp_json_data ut_json_leaf_tab) return integer is l_diffs tt_json_diff_tab := compare_json_data(a_act_json_data,a_exp_json_data); begin forall i in 1..l_diffs.count - insert into ut_json_data_diff_tmp - (diff_id, difference_type,act_element_name,act_element_value,act_json_type,act_access_path, - exp_element_name,exp_element_value,exp_json_type,exp_access_path) - values - (a_diff_id,l_diffs(i).difference_type, - l_diffs(i).act_element_name,ut_utils.to_string(l_diffs(i).act_element_value,null),l_diffs(i).act_json_type, l_diffs(i).act_access_path, - l_diffs(i).exp_element_name,ut_utils.to_string(l_diffs(i).exp_element_value,null),l_diffs(i).exp_json_type,l_diffs(i).exp_access_path); + insert into ut_json_data_diff_tmp ( + diff_id, difference_type, + act_element_name, act_element_value, act_json_type, act_access_path, act_parent_path, + exp_element_name, exp_element_value, exp_json_type, exp_access_path, exp_parent_path + ) + values ( + a_diff_id,l_diffs(i).difference_type, + l_diffs(i).act_element_name,l_diffs(i).act_element_value,l_diffs(i).act_json_type, l_diffs(i).act_access_path, l_diffs(i).act_parent_path, + l_diffs(i).exp_element_name,l_diffs(i).exp_element_value,l_diffs(i).exp_json_type,l_diffs(i).exp_access_path, l_diffs(i).exp_parent_path + ); return l_diffs.count; end; @@ -718,10 +738,13 @@ create or replace package body ut_compound_data_helper is function get_json_diffs_tmp(a_diff_id raw) return tt_json_diff_tab is l_diffs tt_json_diff_tab; begin - select difference_type,act_element_name,act_element_value,act_json_type,act_access_path, - exp_element_name,exp_element_value,exp_json_type,exp_access_path + select difference_type, + act_element_name, act_element_value, act_json_type, act_access_path, act_parent_path, + exp_element_name, exp_element_value, exp_json_type, exp_access_path, exp_parent_path bulk collect into l_diffs - from ut_json_data_diff_tmp where diff_id = a_diff_id; + from ut_json_data_diff_tmp + where diff_id = a_diff_id; + return l_diffs; end; diff --git a/source/expectations/data_values/ut_compound_data_helper.pks b/source/expectations/data_values/ut_compound_data_helper.pks index 9ab74b224..c6b6507fc 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pks +++ b/source/expectations/data_values/ut_compound_data_helper.pks @@ -62,10 +62,12 @@ create or replace package ut_compound_data_helper authid definer is act_element_value varchar2(4000), act_json_type varchar2(4000), act_access_path varchar2(4000), + act_parent_path varchar2(4000), exp_element_name varchar2(4000), exp_element_value varchar2(4000), exp_json_type varchar2(4000), - exp_access_path varchar2(4000) + exp_access_path varchar2(4000), + exp_parent_path varchar2(4000) ); type tt_json_diff_tab is table of t_json_diff_rec; diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb index 1112f8793..ee761b141 100644 --- a/source/expectations/data_values/ut_data_value_json.tpb +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -59,33 +59,34 @@ create or replace type body ut_data_value_json as l_message varchar2(32767); function get_diff_by_type(a_diff ut_compound_data_helper.tt_json_diff_tab) return clob is - l_diff_summary ut_compound_data_helper.tt_json_diff_type_tab := ut_compound_data_helper.get_json_diffs_type(a_diff); - l_message_list ut_varchar2_list := ut_varchar2_list(); + l_diff_summary ut_compound_data_helper.tt_json_diff_type_tab := ut_compound_data_helper.get_json_diffs_type(a_diff); + l_message_list ut_varchar2_list := ut_varchar2_list(); begin for i in 1..l_diff_summary.count loop l_message_list.extend; l_message_list(l_message_list.last) := l_diff_summary(i).no_of_occurence||' '||l_diff_summary(i).difference_type; end loop; - return ut_utils.table_to_clob(l_message_list,','); + return ut_utils.table_to_clob(l_message_list,', '); end; function get_json_diff_text (a_json_diff ut_compound_data_helper.t_json_diff_rec) return clob is begin return - case a_json_diff.difference_type - when ut_compound_data_helper.gc_json_missing + case + when a_json_diff.difference_type = ut_compound_data_helper.gc_json_missing then case - when a_json_diff.act_element_name is not null then q'[Missing property ']'||a_json_diff.act_element_name||q'[']' - when a_json_diff.exp_element_name is not null then q'[Extra property ']'||a_json_diff.exp_element_name||q'[']' - else 'Unknown' - end - when ut_compound_data_helper.gc_json_type - then q'[Actual type is ']'||a_json_diff.act_json_type||q'[' was expected to be ']'||a_json_diff.exp_json_type||q'[']' - when ut_compound_data_helper.gc_json_notequal - then q'[Actual value is ']'||a_json_diff.act_element_value||q'[' was expected to be ']'||a_json_diff.exp_element_value||q'[']' - else 'Unknown' - end || ' on path :'||nvl(a_json_diff.act_access_path,a_json_diff.exp_access_path); + when a_json_diff.act_element_name is not null then q'[ Missing property: ]'||a_json_diff.act_element_name + when a_json_diff.exp_element_name is not null then q'[ Extra property: ]'||a_json_diff.exp_element_name + end || ' on path: '||nvl(a_json_diff.act_parent_path,a_json_diff.exp_parent_path) + else + case + when a_json_diff.difference_type = ut_compound_data_helper.gc_json_type + then q'[ Actual type: ']'||a_json_diff.act_json_type||q'[' was expected to be: ']'||a_json_diff.exp_json_type||q'[']' + when a_json_diff.difference_type = ut_compound_data_helper.gc_json_notequal + then q'[ Actual value: ]'||a_json_diff.act_element_value||q'[ was expected to be: ]'||a_json_diff.exp_element_value + end || ' on path: '||nvl(a_json_diff.act_access_path,a_json_diff.exp_access_path) + end; end; begin @@ -99,13 +100,13 @@ create or replace type body ut_data_value_json as if not l_self.is_null and not l_other.is_null then l_diffs := ut_compound_data_helper.get_json_diffs_tmp(l_diff_id); - l_message:= chr(10)||'Found: '||l_diffs.count|| ' differences' || - case when l_diffs.count > c_max_rows then ', showing first '||c_max_rows else null end||chr(10); + l_message := ' '||l_diffs.count|| ' differences found' || + case when l_diffs.count > c_max_rows then ', showing first '|| c_max_rows else null end||chr(10); ut_utils.append_to_clob( l_result, l_message ); - l_message:= get_diff_by_type(l_diffs)||chr(10); + l_message := get_diff_by_type(l_diffs)||chr(10); ut_utils.append_to_clob( l_result, l_message ); - - for i in 1..least(c_max_rows,l_diffs.count) loop + + for i in 1 .. least( c_max_rows, l_diffs.count ) loop l_results.extend; l_results(l_results.last) := get_json_diff_text(l_diffs(i)); end loop; @@ -133,10 +134,15 @@ create or replace type body ut_data_value_json as l_diff_id ut_compound_data_helper.t_hash; begin if a_other is of (ut_data_value_json) then - l_other := treat(a_other as ut_data_value_json); - l_diff_id := ut_compound_data_helper.get_hash(self.data_id||l_other.data_id); - l_result := case when ut_compound_data_helper.insert_json_diffs( - l_diff_id,self.json_tree.json_tree_info,l_other.json_tree.json_tree_info) > 0 then 1 else 0 end; + l_other := treat(a_other as ut_data_value_json); + l_diff_id := ut_compound_data_helper.get_hash(self.data_id||l_other.data_id); + l_result := + case + when ut_compound_data_helper.insert_json_diffs( + l_diff_id, self.json_tree.json_tree_info, l_other.json_tree.json_tree_info + ) > 0 then 1 + else 0 + end; end if; return l_result; end; diff --git a/source/expectations/data_values/ut_json_data_diff_tmp.sql b/source/expectations/data_values/ut_json_data_diff_tmp.sql index feb6d44c6..08de6ccf8 100644 --- a/source/expectations/data_values/ut_json_data_diff_tmp.sql +++ b/source/expectations/data_values/ut_json_data_diff_tmp.sql @@ -12,14 +12,16 @@ create global temporary table ut_json_data_diff_tmp( See the License for the specific language governing permissions and limitations under the License. */ - diff_id raw(128), - difference_type varchar2(250), - act_element_name varchar2(2000), + diff_id raw(128), + difference_type varchar2(250), + act_element_name varchar2(2000), act_element_value varchar2(4000), - act_json_type varchar2(100), - act_access_path varchar2(4000), - exp_element_name varchar2(2000), + act_json_type varchar2(100), + act_access_path varchar2(4000), + act_parent_path varchar2(4000), + exp_element_name varchar2(2000), exp_element_value varchar2(4000), - exp_json_type varchar2(2000), - exp_access_path varchar2(4000) + exp_json_type varchar2(2000), + exp_access_path varchar2(4000), + exp_parent_path varchar2(4000) ) on commit preserve rows; diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index d295ea147..f2fe71844 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -1,146 +1,184 @@ create or replace type body ut_json_tree_details as - member function get_json_type(a_json_piece json_element_t) return varchar2 is - begin - return case - when a_json_piece.is_object then 'object' - when a_json_piece.is_array then 'array' - when a_json_piece.is_string then 'string' - when a_json_piece.is_number then 'number' - when a_json_piece.is_boolean then 'boolean' - when a_json_piece.is_true then 'true' - when a_json_piece.is_false then 'false' - when a_json_piece.is_null then 'null' - when a_json_piece.is_date then 'date' - when a_json_piece.is_timestamp then 'timestamp' - when a_json_piece.is_scalar then 'scalar' - else null - end; - end; + member function get_json_type(a_json_piece json_element_t) return varchar2 is + begin + return + case + when a_json_piece.is_object then 'object' + when a_json_piece.is_array then 'array' + when a_json_piece.is_string then 'string' + when a_json_piece.is_number then 'number' + when a_json_piece.is_boolean then 'boolean' + when a_json_piece.is_true then 'true' + when a_json_piece.is_false then 'false' + when a_json_piece.is_null then 'null' + when a_json_piece.is_date then 'date' + when a_json_piece.is_timestamp then 'timestamp' + when a_json_piece.is_scalar then 'scalar' + else null + end; + end; - member function get_json_value(a_json_piece json_element_t,a_key varchar2) return varchar2 is + member function get_json_value(a_json_piece json_element_t, a_key varchar2) return varchar2 is l_json_el json_element_t; l_val varchar2(4000); begin - l_json_el := treat(a_json_piece as json_object_t).get (a_key); + l_json_el := treat(a_json_piece as json_object_t).get(a_key); case - when l_json_el.is_string then l_val := treat(a_json_piece as json_object_t).get_string(a_key); - when l_json_el.is_number then l_val := to_char(treat(a_json_piece as json_object_t).get_number(a_key)); - when l_json_el.is_boolean then l_val := ut_utils.to_string(treat(a_json_piece as json_object_t).get_boolean(a_key)); - when l_json_el.is_true then l_val := ut_utils.to_string(treat(a_json_piece as json_object_t).get_boolean(a_key)); - when l_json_el.is_false then l_val := ut_utils.to_string(treat(a_json_piece as json_object_t).get_boolean(a_key)); - when l_json_el.is_date then l_val := to_char(treat(a_json_piece as json_object_t).get_date(a_key),'DD/MM/RRRR'); - when l_json_el.is_timestamp then l_val := to_char(treat(a_json_piece as json_object_t).get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); + when l_json_el.is_string then l_val := ut_utils.to_string(l_json_el.to_string(),null); + when l_json_el.is_number then l_val := ut_utils.to_string(l_json_el.to_number()); + when l_json_el.is_boolean then l_val := ut_utils.to_string(l_json_el.to_boolean()); + when l_json_el.is_true then l_val := ut_utils.to_string(l_json_el.to_boolean()); + when l_json_el.is_false then l_val := ut_utils.to_string(l_json_el.to_boolean()); + when l_json_el.is_date then l_val := ut_utils.to_string(l_json_el.to_date()); + when l_json_el.is_timestamp then l_val := ut_utils.to_string(l_json_el.to_date()); else null; end case; return l_val; end; - member function get_json_value(a_json_piece json_element_t,a_key integer) return varchar2 is + member function get_json_value(a_json_piece json_element_t, a_key integer) return varchar2 is l_json_el json_element_t; l_val varchar2(4000); begin - l_json_el := treat(a_json_piece as json_array_t).get (a_key); + l_json_el := treat(a_json_piece as json_array_t).get(a_key); case - when l_json_el.is_string then l_val := treat(a_json_piece as json_array_t).get_string(a_key); - when l_json_el.is_number then l_val := to_char(treat(a_json_piece as json_array_t).get_number(a_key)); - when l_json_el.is_boolean then l_val := ut_utils.to_string(treat(a_json_piece as json_array_t).get_boolean(a_key)); - when l_json_el.is_true then l_val := ut_utils.to_string(treat(a_json_piece as json_array_t).get_boolean(a_key)); - when l_json_el.is_false then l_val := ut_utils.to_string(treat(a_json_piece as json_array_t).get_boolean(a_key)); - when l_json_el.is_date then l_val := to_char(treat(a_json_piece as json_array_t).get_date(a_key),'DD/MM/RRRR'); - when l_json_el.is_timestamp then l_val := to_char(treat(a_json_piece as json_array_t).get_date(a_key),'DD/MM/RRRR HH24:MI:SS AM'); + when l_json_el.is_string then l_val := ut_utils.to_string(l_json_el.to_string(),null); + when l_json_el.is_number then l_val := ut_utils.to_string(l_json_el.to_number()); + when l_json_el.is_boolean then l_val := ut_utils.to_string(l_json_el.to_boolean()); + when l_json_el.is_true then l_val := ut_utils.to_string(l_json_el.to_boolean()); + when l_json_el.is_false then l_val := ut_utils.to_string(l_json_el.to_boolean()); + when l_json_el.is_date then l_val := ut_utils.to_string(l_json_el.to_date()); + when l_json_el.is_timestamp then l_val := ut_utils.to_string(l_json_el.to_date()); else null; end case; return l_val; end; - member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, - a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, - a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0, a_parent_path varchar2) is + member procedure add_json_leaf( + self in out nocopy ut_json_tree_details, + a_element_name varchar2, + a_element_value varchar2, + a_parent_name varchar2, + a_access_path varchar2, + a_hierarchy_level integer, + a_index_position integer, + a_json_type varchar2, + a_parent_type varchar2, + a_array_element integer := 0, + a_parent_path varchar2 + ) is begin self.json_tree_info.extend; - self.json_tree_info(self.json_tree_info.last) := ut_json_leaf(a_element_name,a_element_value,a_parent_name,a_access_path, - a_hierarchy_level, a_index_position,a_json_type, a_parent_type, a_array_element, a_parent_path); + self.json_tree_info(self.json_tree_info.last) := + ut_json_leaf( + a_element_name, a_element_value, a_parent_name, a_access_path, + a_hierarchy_level, a_index_position,a_json_type, a_parent_type, + a_array_element, a_parent_path + ); end; - member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, - a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as + member procedure traverse_object( + self in out nocopy ut_json_tree_details, + a_json_piece json_element_t, + a_parent_name varchar2 := null, + a_hierarchy_level integer := 1, + a_access_path varchar2 := '$' + ) as l_keys json_key_list; l_object json_object_t := treat(a_json_piece as json_object_t); + l_path varchar2(32767); + l_type varchar2(50); + l_name varchar2(4000); begin - l_keys := coalesce(l_object.get_keys,json_key_list()); - - for indx in 1 .. l_keys.count - loop - add_json_leaf(l_keys(indx), - get_json_value(l_object,l_keys(indx)), - a_parent_name, - a_access_path||'.'||l_keys(indx), - a_hierarchy_level, - indx, - get_json_type(l_object.get (l_keys(indx))), - 'object', - 0, - a_access_path - ); - case get_json_type(l_object.get(l_keys(indx))) + l_keys := coalesce(l_object.get_keys,json_key_list()); + + for i in 1 .. l_keys.count loop + l_type := get_json_type(l_object.get(l_keys(i))); + l_name := '"'||l_keys(i)||'"'; + l_path := a_access_path||'.'||l_name; + + add_json_leaf( + l_name, + get_json_value(l_object,l_keys(i)), + a_parent_name, + l_path, + a_hierarchy_level, + i, + l_type, + 'object', + 0, + a_access_path + ); + case l_type when 'array' then - traverse_array ( - treat (l_object.get (l_keys(indx)) as json_array_t), - l_keys(indx), - a_hierarchy_level + 1, - a_access_path||'.'||l_keys(indx) - ); + traverse_array ( + treat (l_object.get (l_keys(i)) as json_array_t), + l_name, + a_hierarchy_level + 1, + l_path + ); when 'object' then - traverse_object( treat (l_object.get (l_keys(indx)) as json_object_t), - l_keys (indx), - a_hierarchy_level+1, - a_access_path||'.'||l_keys(indx) - ); - else null; + traverse_object( + treat (l_object.get (l_keys(i)) as json_object_t), + l_name, + a_hierarchy_level+1, + l_path + ); + else + null; end case; end loop; end traverse_object; - member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, - a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ) as + member procedure traverse_array( + self in out nocopy ut_json_tree_details, + a_json_piece json_element_t, + a_parent_name varchar2 := null, + a_hierarchy_level integer := 1, + a_access_path varchar2 := '$' + ) as l_array json_array_t; l_type varchar2(50); - l_name varchar2(4000); + l_name varchar2(4000); + l_path varchar2(32767); begin l_array := treat(a_json_piece as json_array_t); - for indx in 0 .. l_array.get_size - 1 - loop - l_type := get_json_type(l_array.get (indx)); - l_name := case when l_type = 'object' then l_type else l_array.get(indx).stringify end; - - add_json_leaf(l_name, - get_json_value(a_json_piece,indx), - a_parent_name, - a_access_path||'['||indx||']', - a_hierarchy_level, - indx, - l_type, - 'array', - 1, - a_access_path - ); + + for i in 0 .. l_array.get_size - 1 loop + l_type := get_json_type(l_array.get(i)); + l_name := case when l_type = 'object' then l_type else l_array.get(i).stringify end; + l_path := a_access_path||'['||i||']'; + + add_json_leaf( + l_name, + get_json_value(a_json_piece,i), + a_parent_name, + l_path, + a_hierarchy_level, + i, + l_type, + 'array', + 1, + l_path + ); case l_type when 'array' then - traverse_array ( - treat (l_array.get (indx) as json_array_t), - l_name, - a_hierarchy_level + 1, - a_access_path||'['||indx||']' - ); + traverse_array ( + treat (l_array.get (i) as json_array_t), + l_name, + a_hierarchy_level + 1, + l_path + ); when 'object' then - traverse_object( - treat (l_array.get (indx) as json_object_t), - l_name, - a_hierarchy_level+1, - a_access_path||'['||indx||']' - ); - else null; + traverse_object( + treat (l_array.get (i) as json_object_t), + l_name, + a_hierarchy_level + 1, + l_path + ); + else + null; end case; end loop; end traverse_array; @@ -148,9 +186,9 @@ create or replace type body ut_json_tree_details as member procedure init(self in out nocopy ut_json_tree_details,a_json_doc in json_element_t, a_level_in integer := 0) is begin if a_json_doc.is_object then - traverse_object(treat (a_json_doc as json_object_t),null,1,'$'); + traverse_object(treat (a_json_doc as json_object_t)); elsif a_json_doc.is_array then - traverse_array(treat (a_json_doc as json_array_t),null,1,'$'); + traverse_array(treat (a_json_doc as json_array_t)); end if; end; diff --git a/source/expectations/data_values/ut_json_tree_details.tps b/source/expectations/data_values/ut_json_tree_details.tps index f2aa028f7..8e04e8b83 100644 --- a/source/expectations/data_values/ut_json_tree_details.tps +++ b/source/expectations/data_values/ut_json_tree_details.tps @@ -15,20 +15,40 @@ create or replace type ut_json_tree_details force as object ( See the License for the specific language governing permissions and limitations under the License. */ - json_tree_info ut_json_leaf_tab, - member function get_json_type(a_json_piece json_element_t) return varchar2, - member function get_json_value(a_json_piece json_element_t,a_key varchar2) return varchar2, - member function get_json_value(a_json_piece json_element_t,a_key integer) return varchar2, - member procedure add_json_leaf(self in out nocopy ut_json_tree_details, a_element_name varchar2, a_element_value varchar2, - a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, - a_json_type in varchar2, a_parent_type in varchar2, a_array_element integer := 0,a_parent_path varchar2), - member procedure traverse_object(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, - a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ), - member procedure traverse_array(self in out nocopy ut_json_tree_details, a_json_piece json_element_t, - a_parent_name varchar2 := null, a_hierarchy_level integer := 1, a_access_path varchar2 := null ), - member procedure init(self in out nocopy ut_json_tree_details,a_json_doc in json_element_t, a_level_in integer := 0), - constructor function ut_json_tree_details( - self in out nocopy ut_json_tree_details,a_json_doc in json_element_t, a_level_in integer := 0 - ) return self as result + json_tree_info ut_json_leaf_tab, + member function get_json_type(a_json_piece json_element_t) return varchar2, + member function get_json_value(a_json_piece json_element_t,a_key varchar2) return varchar2, + member function get_json_value(a_json_piece json_element_t,a_key integer) return varchar2, + member procedure add_json_leaf( + self in out nocopy ut_json_tree_details, + a_element_name varchar2, + a_element_value varchar2, + a_parent_name varchar2, + a_access_path varchar2, + a_hierarchy_level integer, + a_index_position integer, + a_json_type in varchar2, + a_parent_type in varchar2, + a_array_element integer := 0, + a_parent_path varchar2 + ), + member procedure traverse_object( + self in out nocopy ut_json_tree_details, + a_json_piece json_element_t, + a_parent_name varchar2 := null, + a_hierarchy_level integer := 1, + a_access_path varchar2 := '$' + ), + member procedure traverse_array( + self in out nocopy ut_json_tree_details, + a_json_piece json_element_t, + a_parent_name varchar2 := null, + a_hierarchy_level integer := 1, + a_access_path varchar2 := '$' + ), + member procedure init(self in out nocopy ut_json_tree_details,a_json_doc in json_element_t, a_level_in integer := 0), + constructor function ut_json_tree_details( + self in out nocopy ut_json_tree_details, a_json_doc in json_element_t, a_level_in integer := 0 + ) return self as result ) / diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index e42925196..6214d2237 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -68,28 +68,28 @@ create or replace package body test_expectations_json is --Act ut3.ut.expect( l_actual ).to_equal( l_expected ); --Assert - l_expected_message := q'[%Found: 20 differences -%3 incorrect types,4 unequal values,13 missing properties -%Missing property 'Alexander Skarsg?rd' on path :$.Alexander Skarsg?rd -%Extra property 'Alexander Skarsgard' on path :$.Alexander Skarsgard -%Missing property 'Alice Farmer' on path :$.Alice Farmer -%Extra property 'Clarke Peters' on path :$.Clarke Peters -%Extra property 'one' on path :$.Amy Ryan.one -%Missing property '"The Sopranos"' on path :$.Annie Fitzgerald[2] -%Extra property 'two' on path :$.Amy Ryan.two -%Missing property '"Oz"' on path :$.Annie Fitzgerald[3] -%Missing property 'otherint' on path :$.Aidan Gillen.otherint -%Extra property 'object1' on path :$.Aidan Gillen.object.object1 -%Extra property 'object2' on path :$.Aidan Gillen.object.object2 -%Extra property 'object3' on path :$.Aidan Gillen.object.object3 -%Extra property 'object4' on path :$.Aidan Gillen.object.object4 -%Actual type is 'array' was expected to be 'object' on path :$.Amy Ryan -%Actual type is 'string' was expected to be 'number' on path :$.Aidan Gillen.int -%Actual type is 'string' was expected to be 'boolean' on path :$.Aidan Gillen.aboolean -%Actual value is 'True Blood' was expected to be 'Big Love' on path :$.Annie Fitzgerald[0] -%Actual value is 'Big Love' was expected to be 'True Blood' on path :$.Annie Fitzgerald[1] -%Actual value is 'FALSE' was expected to be 'TRUE' on path :$.Aidan Gillen.boolean -%Actual value is 'Game of Thrones' was expected to be 'Game of Thron"es' on path :$.Aidan Gillen.array[0]%]'; + l_expected_message := q'[%Diff: 20 differences found +%3 incorrect types, 4 unequal values, 13 missing properties +%Missing property: "Alexander Skarsg?rd" on path: $ +%Extra property: "Alexander Skarsgard" on path: $ +%Missing property: "Alice Farmer" on path: $ +%Extra property: "Clarke Peters" on path: $ +%Extra property: "one" on path: $."Amy Ryan" +%Missing property: "The Sopranos" on path: $."Annie Fitzgerald"[2] +%Extra property: "two" on path: $."Amy Ryan" +%Missing property: "Oz" on path: $."Annie Fitzgerald"[3] +%Missing property: "otherint" on path: $."Aidan Gillen" +%Extra property: "object1" on path: $."Aidan Gillen"."object" +%Extra property: "object2" on path: $."Aidan Gillen"."object" +%Extra property: "object3" on path: $."Aidan Gillen"."object" +%Extra property: "object4" on path: $."Aidan Gillen"."object" +%Actual type: 'array' was expected to be: 'object' on path: $."Amy Ryan" +%Actual type: 'string' was expected to be: 'number' on path: $."Aidan Gillen"."int" +%Actual type: 'string' was expected to be: 'boolean' on path: $."Aidan Gillen"."aboolean" +%Actual value: "True Blood" was expected to be: "Big Love" on path: $."Annie Fitzgerald"[0] +%Actual value: "Big Love" was expected to be: "True Blood" on path: $."Annie Fitzgerald"[1] +%Actual value: FALSE was expected to be: TRUE on path: $."Aidan Gillen"."boolean" +%Actual value: "Game of Thrones" was expected to be: "Game of Thron\"es" on path: $."Aidan Gillen"."array"[0]%]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert ut.expect(l_actual_message).to_be_like(l_expected_message); @@ -446,10 +446,9 @@ create or replace package body test_expectations_json is .stringify,'$.Actors[1].children'))); --Assert l_expected_message := q'[%Actual: json was expected to equal: json -%Diff: -%Found: 1 differences +%Diff: 1 differences found %1 unequal values -%Actual value is 'Noemi' was expected to be 'Indio Falconer' on path :$[0]%]'; +%Actual value: "Noemi" was expected to be: "Indio Falconer" on path: $[0]%]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert ut.expect(l_actual_message).to_be_like(l_expected_message); @@ -1557,28 +1556,28 @@ create or replace package body test_expectations_json is --Act ut3.ut.expect( l_actual ).to_equal( l_expected ); --Assert - l_expected_message := q'[%Found: 133 differences, showing first 20 -%132 unequal values,1 missing properties -%Extra property 'object' on path :$[5] -%Actual value is '5ce6ec46cb9977b050f15d97' was expected to be '5ce6ec6660565269b16cf836' on path :$[0]._id -%Actual value is '5ce6ec469ba57bef5c421021' was expected to be '5ce6ec66383ddbf3c400e3ed' on path :$[1]._id -%Actual value is '5ce6ec4632328a654d592cb6' was expected to be '5ce6ec6600fb7aaee2d1243e' on path :$[3]._id -%Actual value is '5ce6ec464e6f8751e75ed29f' was expected to be '5ce6ec660a8b5f95ed543305' on path :$[2]._id -%Actual value is '5ce6ec46d9dbfbf9b184cee7' was expected to be '5ce6ec660585cbb589b34fc8' on path :$[4]._id -%Actual value is '59be5b73-fffe-4a4f-acea-65c5abbdb53c' was expected to be '2e778803-50d3-411f-b34d-47d0f19d03f7' on path :$[1].guid -%Actual value is '9dece65b-6b48-4960-880b-7795ff63c81c' was expected to be '18547241-6fd0-466d-9f79-21aeb0485294' on path :$[4].guid -%Actual value is '42e07b71-b769-4078-b226-f79048b75bd2' was expected to be 'bb0eaa88-f7fd-4b72-8538-8c0b4595bcec' on path :$[2].guid -%Actual value is '6b9124a9-fbde-4c60-8dac-e296f5daa3c4' was expected to be '4a4363b5-9d65-4b22-9b58-a5c8c1c5bd5d' on path :$[3].guid -%Actual value is '1acb2b6b-15b5-4747-a62f-db477e18df61' was expected to be 'c222eda5-d925-4163-89e3-4b0e50d5e297' on path :$[0].guid -%Actual value is 'FALSE' was expected to be 'TRUE' on path :$[2].isActive -%Actual value is 'TRUE' was expected to be 'FALSE' on path :$[3].isActive -%Actual value is 'TRUE' was expected to be 'FALSE' on path :$[1].isActive -%Actual value is '$3,895.35' was expected to be '$2,299.28' on path :$[1].balance -%Actual value is '$1,443.80' was expected to be '$3,626.25' on path :$[0].balance -%Actual value is '$3,366.81' was expected to be '$3,085.28' on path :$[2].balance -%Actual value is '$2,927.54' was expected to be '$3,853.86' on path :$[4].balance -%Actual value is '$2,374.96' was expected to be '$3,152.70' on path :$[3].balance -%Actual value is '23' was expected to be '36' on path :$[2].age%]'; + l_expected_message := q'[%Diff: 133 differences found, showing first 20 +%132 unequal values, 1 missing properties +%Extra property: object on path: $[5] +%Actual value: "5ce6ec46cb9977b050f15d97" was expected to be: "5ce6ec6660565269b16cf836" on path: $[0]."_id" +%Actual value: "5ce6ec469ba57bef5c421021" was expected to be: "5ce6ec66383ddbf3c400e3ed" on path: $[1]."_id" +%Actual value: "5ce6ec4632328a654d592cb6" was expected to be: "5ce6ec6600fb7aaee2d1243e" on path: $[3]."_id" +%Actual value: "5ce6ec464e6f8751e75ed29f" was expected to be: "5ce6ec660a8b5f95ed543305" on path: $[2]."_id" +%Actual value: "5ce6ec46d9dbfbf9b184cee7" was expected to be: "5ce6ec660585cbb589b34fc8" on path: $[4]."_id" +%Actual value: "59be5b73-fffe-4a4f-acea-65c5abbdb53c" was expected to be: "2e778803-50d3-411f-b34d-47d0f19d03f7" on path: $[1]."guid" +%Actual value: "9dece65b-6b48-4960-880b-7795ff63c81c" was expected to be: "18547241-6fd0-466d-9f79-21aeb0485294" on path: $[4]."guid" +%Actual value: "42e07b71-b769-4078-b226-f79048b75bd2" was expected to be: "bb0eaa88-f7fd-4b72-8538-8c0b4595bcec" on path: $[2]."guid" +%Actual value: "6b9124a9-fbde-4c60-8dac-e296f5daa3c4" was expected to be: "4a4363b5-9d65-4b22-9b58-a5c8c1c5bd5d" on path: $[3]."guid" +%Actual value: "1acb2b6b-15b5-4747-a62f-db477e18df61" was expected to be: "c222eda5-d925-4163-89e3-4b0e50d5e297" on path: $[0]."guid" +%Actual value: FALSE was expected to be: TRUE on path: $[2]."isActive" +%Actual value: TRUE was expected to be: FALSE on path: $[3]."isActive" +%Actual value: TRUE was expected to be: FALSE on path: $[1]."isActive" +%Actual value: "$3,895.35" was expected to be: "$2,299.28" on path: $[1]."balance" +%Actual value: "$1,443.80" was expected to be: "$3,626.25" on path: $[0]."balance" +%Actual value: "$3,366.81" was expected to be: "$3,085.28" on path: $[2]."balance" +%Actual value: "$2,927.54" was expected to be: "$3,853.86" on path: $[4]."balance" +%Actual value: "$2,374.96" was expected to be: "$3,152.70" on path: $[3]."balance" +%Actual value: 23 was expected to be: 36 on path: $[2]."age"%]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert From 6586342635e966cd7be2ac13e52a70557f7b5bc3 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Mon, 17 Jun 2019 17:22:34 +0100 Subject: [PATCH 24/24] Fixed install on Oracle < 12.2 --- .../data_values/ut_json_tree_details.tpb | 8 +- source/expectations/json_objects_specs.sql | 115 +++++++----------- 2 files changed, 49 insertions(+), 74 deletions(-) diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index f2fe71844..a333ea71c 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -28,8 +28,8 @@ create or replace type body ut_json_tree_details as when l_json_el.is_string then l_val := ut_utils.to_string(l_json_el.to_string(),null); when l_json_el.is_number then l_val := ut_utils.to_string(l_json_el.to_number()); when l_json_el.is_boolean then l_val := ut_utils.to_string(l_json_el.to_boolean()); - when l_json_el.is_true then l_val := ut_utils.to_string(l_json_el.to_boolean()); - when l_json_el.is_false then l_val := ut_utils.to_string(l_json_el.to_boolean()); +-- when l_json_el.is_true then l_val := ut_utils.to_string(l_json_el.to_boolean()); +-- when l_json_el.is_false then l_val := ut_utils.to_string(l_json_el.to_boolean()); when l_json_el.is_date then l_val := ut_utils.to_string(l_json_el.to_date()); when l_json_el.is_timestamp then l_val := ut_utils.to_string(l_json_el.to_date()); else null; @@ -46,8 +46,8 @@ create or replace type body ut_json_tree_details as when l_json_el.is_string then l_val := ut_utils.to_string(l_json_el.to_string(),null); when l_json_el.is_number then l_val := ut_utils.to_string(l_json_el.to_number()); when l_json_el.is_boolean then l_val := ut_utils.to_string(l_json_el.to_boolean()); - when l_json_el.is_true then l_val := ut_utils.to_string(l_json_el.to_boolean()); - when l_json_el.is_false then l_val := ut_utils.to_string(l_json_el.to_boolean()); +-- when l_json_el.is_true then l_val := ut_utils.to_string(l_json_el.to_boolean()); +-- when l_json_el.is_false then l_val := ut_utils.to_string(l_json_el.to_boolean()); when l_json_el.is_date then l_val := ut_utils.to_string(l_json_el.to_date()); when l_json_el.is_timestamp then l_val := ut_utils.to_string(l_json_el.to_date()); else null; diff --git a/source/expectations/json_objects_specs.sql b/source/expectations/json_objects_specs.sql index 3584e58f6..c0d42e8fb 100644 --- a/source/expectations/json_objects_specs.sql +++ b/source/expectations/json_objects_specs.sql @@ -9,85 +9,60 @@ BEGIN STATIC FUNCTION parse(jsn VARCHAR2) RETURN JSON_Element_T, STATIC FUNCTION parse(jsn CLOB) RETURN JSON_Element_T, STATIC FUNCTION parse(jsn BLOB) RETURN JSON_Element_T, - MEMBER FUNCTION to_Clob(self IN JSON_ELEMENT_T) RETURN CLOB, - MEMBER FUNCTION stringify(self IN JSON_ELEMENT_T) RETURN VARCHAR2, - MEMBER FUNCTION is_Object(self IN JSON_ELEMENT_T) RETURN BOOLEAN, - MEMBER FUNCTION is_Array(self IN JSON_ELEMENT_T) RETURN BOOLEAN, - MEMBER FUNCTION is_Scalar(self IN JSON_ELEMENT_T) RETURN BOOLEAN, - MEMBER FUNCTION is_String(self IN JSON_ELEMENT_T) RETURN BOOLEAN, - MEMBER FUNCTION is_Number(self IN JSON_ELEMENT_T) RETURN BOOLEAN, - MEMBER FUNCTION is_Boolean(self IN JSON_ELEMENT_T) RETURN BOOLEAN, - MEMBER FUNCTION is_True(self IN JSON_ELEMENT_T) RETURN BOOLEAN, - MEMBER FUNCTION is_False(self IN JSON_ELEMENT_T) RETURN BOOLEAN, - MEMBER FUNCTION is_Null(self IN JSON_ELEMENT_T) RETURN BOOLEAN, - MEMBER FUNCTION is_Date(self IN JSON_ELEMENT_T) RETURN BOOLEAN, - MEMBER FUNCTION is_Timestamp(self IN JSON_ELEMENT_T) RETURN BOOLEAN, + MEMBER FUNCTION to_Clob RETURN CLOB, + MEMBER FUNCTION stringify RETURN VARCHAR2, + MEMBER FUNCTION is_Object RETURN BOOLEAN, + MEMBER FUNCTION is_Array RETURN BOOLEAN, + MEMBER FUNCTION is_Scalar RETURN BOOLEAN, + MEMBER FUNCTION is_String RETURN BOOLEAN, + MEMBER FUNCTION is_Number RETURN BOOLEAN, + MEMBER FUNCTION is_Boolean RETURN BOOLEAN, + MEMBER FUNCTION is_True RETURN BOOLEAN, + MEMBER FUNCTION is_False RETURN BOOLEAN, + MEMBER FUNCTION is_Null RETURN BOOLEAN, + MEMBER FUNCTION is_Date RETURN BOOLEAN, + MEMBER FUNCTION is_Timestamp RETURN BOOLEAN, + MEMBER FUNCTION to_string RETURN VARCHAR2, + MEMBER FUNCTION to_number RETURN NUMBER, + MEMBER FUNCTION to_boolean RETURN BOOLEAN, + MEMBER FUNCTION to_date RETURN VARCHAR2, MEMBER FUNCTION get_Size(self IN JSON_ELEMENT_T) RETURN NUMBER ) NOT FINAL NOT INSTANTIABLE;]'; execute immediate q'[create or replace TYPE JSON_KEY_LIST FORCE AS VARRAY(32767) OF VARCHAR2(4000);]'; - execute immediate q'[create or replace TYPE JSON_Array_T FORCE AUTHID CURRENT_USER - UNDER JSON_Element_T( - dummy NUMBER, - CONSTRUCTOR FUNCTION JSON_Array_T(self IN OUT JSON_ARRAY_T) - RETURN SELF AS RESULT, - - MEMBER FUNCTION get(self IN JSON_ARRAY_T, pos NUMBER) - RETURN JSON_Element_T, - MEMBER FUNCTION get_String(self IN JSON_ARRAY_T, pos NUMBER) - RETURN VARCHAR2, - MEMBER FUNCTION get_Number(self IN JSON_ARRAY_T, pos NUMBER) - RETURN NUMBER, - MEMBER FUNCTION get_Boolean(self IN JSON_ARRAY_T, pos NUMBER) - RETURN BOOLEAN, - MEMBER FUNCTION get_Date(self IN JSON_ARRAY_T, pos NUMBER) - RETURN DATE, - MEMBER FUNCTION get_Timestamp(self IN JSON_ARRAY_T, pos NUMBER) - RETURN TIMESTAMP, - MEMBER FUNCTION get_Clob(self IN JSON_ARRAY_T, pos NUMBER) RETURN CLOB, - MEMBER PROCEDURE get_Clob(self IN OUT NOCOPY JSON_ARRAY_T, pos NUMBER, - c IN OUT NOCOPY CLOB), - MEMBER FUNCTION get_Blob(self IN JSON_ARRAY_T, pos NUMBER) RETURN BLOB, - MEMBER PROCEDURE get_Blob(self IN OUT NOCOPY JSON_ARRAY_T, pos NUMBER, - b IN OUT NOCOPY BLOB), - MEMBER FUNCTION get_Type(self IN JSON_ARRAY_T, pos NUMBER) - RETURN VARCHAR2 + execute immediate q'[create or replace TYPE JSON_Array_T FORCE AUTHID CURRENT_USER UNDER JSON_Element_T( + CONSTRUCTOR FUNCTION JSON_Array_T RETURN SELF AS RESULT, + MEMBER FUNCTION get(pos NUMBER) RETURN JSON_Element_T, + MEMBER FUNCTION get_String(pos NUMBER) RETURN VARCHAR2, + MEMBER FUNCTION get_Number(pos NUMBER) RETURN NUMBER, + MEMBER FUNCTION get_Boolean(pos NUMBER) RETURN BOOLEAN, + MEMBER FUNCTION get_Date(pos NUMBER) RETURN DATE, + MEMBER FUNCTION get_Timestamp(pos NUMBER) RETURN TIMESTAMP, + MEMBER FUNCTION get_Clob(pos NUMBER) RETURN CLOB, + MEMBER PROCEDURE get_Clob(pos NUMBER, c IN OUT NOCOPY CLOB), + MEMBER FUNCTION get_Blob(pos NUMBER) RETURN BLOB, + MEMBER PROCEDURE get_Blob(pos NUMBER, b IN OUT NOCOPY BLOB), + MEMBER FUNCTION get_Type(pos NUMBER) RETURN VARCHAR2 ) FINAL;]'; execute immediate q'[create or replace TYPE JSON_Object_T AUTHID CURRENT_USER UNDER JSON_Element_T( - dummy NUMBER, - CONSTRUCTOR FUNCTION JSON_Object_T(self IN OUT JSON_OBJECT_T) - RETURN SELF AS RESULT, - - MEMBER FUNCTION get(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN JSON_Element_T, - MEMBER FUNCTION get_Object(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN JSON_OBJECT_T, - MEMBER FUNCTION get_Array(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN JSON_ARRAY_T, - MEMBER FUNCTION get_String(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN VARCHAR2, - MEMBER FUNCTION get_Number(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN NUMBER, - MEMBER FUNCTION get_Boolean(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN BOOLEAN, - MEMBER FUNCTION get_Date(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN DATE, - MEMBER FUNCTION get_Timestamp(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN TIMESTAMP, - MEMBER FUNCTION get_Clob(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN CLOB, - MEMBER PROCEDURE get_Clob(self IN OUT NOCOPY JSON_OBJECT_T, - key VARCHAR2, c IN OUT NOCOPY CLOB), - MEMBER FUNCTION get_Blob(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN BLOB, - MEMBER PROCEDURE get_Blob(self IN OUT NOCOPY JSON_OBJECT_T, - key VARCHAR2, b IN OUT NOCOPY BLOB), - MEMBER FUNCTION get_Type(self IN JSON_OBJECT_T, key VARCHAR2) - RETURN VARCHAR2, - MEMBER FUNCTION get_Keys(self IN JSON_OBJECT_T) RETURN JSON_KEY_LIST + CONSTRUCTOR FUNCTION JSON_Object_T RETURN SELF AS RESULT, + MEMBER FUNCTION get(key VARCHAR2) RETURN JSON_Element_T, + MEMBER FUNCTION get_Object(key VARCHAR2) RETURN JSON_OBJECT_T, + MEMBER FUNCTION get_Array(key VARCHAR2) RETURN JSON_ARRAY_T, + MEMBER FUNCTION get_String(key VARCHAR2) RETURN VARCHAR2, + MEMBER FUNCTION get_Number(key VARCHAR2) RETURN NUMBER, + MEMBER FUNCTION get_Boolean(key VARCHAR2) RETURN BOOLEAN, + MEMBER FUNCTION get_Date(key VARCHAR2) RETURN DATE, + MEMBER FUNCTION get_Timestamp(key VARCHAR2) RETURN TIMESTAMP, + MEMBER FUNCTION get_Clob(key VARCHAR2) RETURN CLOB, + MEMBER PROCEDURE get_Clob(key VARCHAR2, c IN OUT NOCOPY CLOB), + MEMBER FUNCTION get_Blob(key VARCHAR2) RETURN BLOB, + MEMBER PROCEDURE get_Blob(key VARCHAR2, b IN OUT NOCOPY BLOB), + MEMBER FUNCTION get_Type(key VARCHAR2) RETURN VARCHAR2, + MEMBER FUNCTION get_Keys RETURN JSON_KEY_LIST ) FINAL;]'; $end