diff --git a/development/cleanup.sh b/development/cleanup.sh index 313e382ca..8cee4570e 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 4104e2672..86d7e4da2 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 diff --git a/docs/userguide/advanced_data_comparison.md b/docs/userguide/advanced_data_comparison.md index 4a6ad6bce..21da173aa 100644 --- a/docs/userguide/advanced_data_comparison.md +++ b/docs/userguide/advanced_data_comparison.md @@ -7,10 +7,11 @@ 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 +Syntax ``` ut.expect( a_actual {data-type} ).to_( equal( a_expected {data-type})[.extendend_option()[.extendend_option()[...]]]); @@ -503,3 +504,5 @@ Finished in .046193 seconds 1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` + + diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index fac7c7c80..fcfdc9dc9 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -319,7 +319,7 @@ end; ## have_count Unary matcher that validates if the provided dataset count is equal to expected value. -Can be used with `refcursor` or `table type` +Can be used with `refcursor` , `json`or `table type` Usage: ```sql @@ -1177,7 +1177,218 @@ 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 + + + +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 is + 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; + l_array_actual json_array_t; + l_array_expected json_array_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" + ] + } + }' + ); + + 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(l_array_actual).to_equal(l_array_expected); + + end; +end; +/ +``` + + + + + + + # 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: @@ -1211,21 +1422,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/api/ut.pkb b/source/api/ut.pkb index 1a95f0725..27d33835e 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 17b4a845e..5549a75c2 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/core/ut_utils.pkb b/source/core/ut_utils.pkb index 3b6a09ddf..6d303ba4d 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -490,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/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_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index 1d5686f03..5a48708dd 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -635,6 +635,96 @@ create or replace package body ut_compound_data_helper is end; end; + 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 + 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) + ) + 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) + 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 + 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); + + return l_diffs.count; + 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 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'; 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..9ab74b224 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,28 @@ 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), + act_access_path varchar2(4000), + exp_element_name varchar2(4000), + exp_element_value varchar2(4000), + exp_json_type varchar2(4000), + exp_access_path 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 +125,14 @@ create or replace package ut_compound_data_helper authid definer is */ function type_no_length ( a_type_name varchar2) return boolean; + 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; + end; / 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..1112f8793 --- /dev/null +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -0,0 +1,160 @@ +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. + */ + + 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; + 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(); + 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; + + overriding member function is_null return boolean is + begin + 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); + 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_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); + + 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(); + 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 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 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); + end; + + begin + 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); + + 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); + 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..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; + 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); + return l_result_string; + end; + + overriding member function compare_implementation(a_other ut_data_value) return integer is + 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 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); + 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 new file mode 100644 index 000000000..50f721bf6 --- /dev/null +++ b/source/expectations/data_values/ut_data_value_json.tps @@ -0,0 +1,32 @@ +create or replace type ut_data_value_json under ut_compound_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. + */ + 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, + 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 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..feb6d44c6 --- /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 varchar2(4000), + act_json_type varchar2(100), + act_access_path varchar2(4000), + exp_element_name varchar2(2000), + 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_leaf.tpb b/source/expectations/data_values/ut_json_leaf.tpb new file mode 100644 index 000000000..83f1009ef --- /dev/null +++ b/source/expectations/data_values/ut_json_leaf.tpb @@ -0,0 +1,32 @@ +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, a_parent_path varchar2) 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; + 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_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_parent_path); + 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..b283367a4 --- /dev/null +++ b/source/expectations/data_values/ut_json_leaf.tps @@ -0,0 +1,42 @@ +create or replace type ut_json_leaf 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), + 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_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_path varchar2) + 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..d295ea147 --- /dev/null +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -0,0 +1,169 @@ +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_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); + 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'); + 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 + 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); + 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'); + 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 + 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); + 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 + l_keys json_key_list; + l_object json_object_t := treat(a_json_piece as json_object_t); + 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))) + 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) + ); + 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; + 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 + l_array json_array_t; + l_type varchar2(50); + l_name varchar2(4000); + 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 + ); + 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||']' + ); + 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; + 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..f2aa028f7 --- /dev/null +++ b/source/expectations/data_values/ut_json_tree_details.tps @@ -0,0 +1,34 @@ +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 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 +) +/ 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/expectations/matchers/ut_equal.tpb b/source/expectations/matchers/ut_equal.tpb index a666caaa3..9df566af7 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 @@ -237,17 +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 ); + || 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/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/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.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..d015aa5f4 --- /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 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; + + 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 new file mode 100644 index 000000000..d032d7851 --- /dev/null +++ b/source/expectations/ut_expectation_json.tps @@ -0,0 +1,29 @@ +create or replace type ut_expectation_json 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 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 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 e6ccf7d28..842172a5f 100644 --- a/source/install.sql +++ b/source/install.sql @@ -34,6 +34,7 @@ alter session set current_schema = &&ut3_owner; @@check_sys_grants.sql "'CREATE TYPE','CREATE VIEW','CREATE SYNONYM','CREATE SEQUENCE','CREATE PROCEDURE','CREATE TABLE'" --set define off + --dbms_output buffer cache table @@install_component.sql 'core/ut_dbms_output_cache.sql' @@ -84,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' @@ -196,7 +199,11 @@ 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' +@@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' @@ -215,6 +222,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' @@ -233,9 +241,12 @@ 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' +@@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' @@ -256,6 +267,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' @@ -275,6 +287,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/source/uninstall_objects.sql b/source/uninstall_objects.sql index f34b964de..a7e6b947e 100644 --- a/source/uninstall_objects.sql +++ b/source/uninstall_objects.sql @@ -82,6 +82,8 @@ drop table ut_dbms_output_cache purge; drop type ut_expectation_compound force; +drop type ut_expectation_json force; + drop type ut_expectation force; drop package ut_expectation_processor; @@ -132,6 +134,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; @@ -152,6 +156,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; @@ -162,6 +172,8 @@ drop table ut_compound_data_tmp purge; drop table ut_compound_data_diff_tmp purge; +drop table ut_json_data_diff_tmp; + drop trigger ut_trigger_annotation_parsing; drop package ut_annotation_manager; @@ -198,6 +210,15 @@ drop package ut_metadata; drop package ut_ansiconsole_helper; +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; drop sequence ut_savepoint_seq; diff --git a/test/install_ut3_user_tests.sql b/test/install_ut3_user_tests.sql index 67014f815..b7361e4aa 100644 --- a/test/install_ut3_user_tests.sql +++ b/test/install_ut3_user_tests.sql @@ -23,6 +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 @@ -61,6 +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 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..e42925196 --- /dev/null +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -0,0 +1,1610 @@ +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_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 ); + --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('{"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'[%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_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 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; + begin + -- Arrange + l_expected := json_object_t(); + + --Act + ut3.ut.expect( l_expected ).to_be_empty; + --Assert + 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; + + 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; + + 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; + + 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; + + 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; + + 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 new file mode 100644 index 000000000..64dff42e8 --- /dev/null +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -0,0 +1,79 @@ +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 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; + + --%test( Fail json string is empty) + procedure fail_empty_json; + + --%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; + + --%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; + + --%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; + + --%test( Compare two objects json ) + procedure check_json_objects; + + --%test( Compare two json arrays ) + procedure check_json_arrays; + +end; +/