Files
dc/sas/sasjs/macros/mpe_derived.test.sas
T
dc 9a047ad42e
Build / Build-and-ng-test (pull_request) Successful in 6m20s
Lighthouse Checks / lighthouse (pull_request) Successful in 22m5s
Build / Build-and-test-development (pull_request) Successful in 32m21s
fix(derived): repair the validation rows for MPE_DERIVED
Four stray insert lines and a missing one left the MPE_VALIDATIONS block for
MPE_DERIVED malformed, so makedata aborted and the control table was never
created - found by building the library on a real estate.

The format assertions in the test now read the emitted statement, because proc
contents on a view does not report a format in full ('DATE' for DATE9.).
2026-10-21 12:06:35 +00:00

297 lines
6.5 KiB
SAS

/**
@file
@brief testing the mpe_derived macro
@details Covers the generated statements, the view, the metadata the callers
consume, and the dependency rules.
The configuration checks abort the run when they fail, so they are not
exercised here - this suite covers the behaviour that callers depend on.
<h4> SAS Macros </h4>
@li mf_existds.sas
@li mp_assert.sas
@li mp_assertcols.sas
@li mp_assertdsobs.sas
@li mp_assertscope.sas
@li mpe_derived.sas
@author 4GL Apps Ltd
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
and may not be re-distributed or re-sold without the express permission of
4GL Apps Ltd.
**/
/* a source table, and a table with no derived variables */
data work.class;
set sashelp.class;
run;
data work.noconfig;
set sashelp.class;
run;
/* clean up any config left behind by an earlier run */
proc sql;
delete from &dclib..mpe_derived where upcase(libref)='WORK';
quit;
/**
* Test 1 - statements, view, chaining
*/
proc sql;
insert into &dclib..mpe_derived set
tx_from=0
,tx_to='31DEC5999:23:59:59'dt
,libref='WORK'
,dsn='CLASS'
,var_nm='BAND'
,var_deps='AGE'
,var_type='C'
,var_len=8
,var_fmt=''
,var_label='Age band'
,var_logic="ifc(AGE>13,'TEEN','JUNIOR')"
,active=1;
insert into &dclib..mpe_derived set
tx_from=0
,tx_to='31DEC5999:23:59:59'dt
,libref='WORK'
,dsn='CLASS'
,var_nm='BAND_AGE'
,var_deps='BAND AGE'
,var_type='C'
,var_len=16
,var_fmt=''
,var_logic="cats(BAND,'-',AGE)"
,active=1;
insert into &dclib..mpe_derived set
tx_from=0
,tx_to='31DEC5999:23:59:59'dt
,libref='WORK'
,dsn='CLASS'
,var_nm='DOUBLE_AGE'
,var_deps='AGE'
,var_type='N'
,var_len=8
,var_fmt=''
,var_logic='AGE * 2'
,active=1;
quit;
/* lower case libds on purpose - the config match must be case insensitive */
/* the setup SQL above creates the SQL automatic macro variables, so the
snapshot is taken here - the check is for the macro's own leakage */
%mp_assertscope(SNAPSHOT)
%mpe_derived(libds=work.class,
dclib=&dclib,
outref=derref1,
outview=work.vw_derived,
outvars=work.derived_vars
)
%mp_assertdsobs(work.derived_vars,
desc=Test1 - one row per derived variable,
test=EQUALS 3,
outds=work.test_results
)
%mp_assertcols(work.vw_derived,
desc=Test1 - the view carries the derived columns,
cols=BAND BAND_AGE DOUBLE_AGE,
test=ALL,
outds=work.test_results
)
data work.chk1;
set work.vw_derived;
where BAND='TEEN';
run;
%mp_assertdsobs(work.chk1,
desc=Test1 - the filter on a derived column selects rows,
test=HASOBS,
outds=work.test_results
)
/* the chained value only resolves if the statements run in order */
data work.chk1b;
set work.vw_derived;
where BAND='TEEN';
if BAND_AGE ne cats(BAND,'-',AGE) then output;
run;
%mp_assertdsobs(work.chk1b,
desc=Test1 - a derived variable can reference an earlier one,
test=EQUALS 0,
outds=work.test_results
)
data work.chk1c;
set work.vw_derived;
if DOUBLE_AGE ne AGE*2 then output;
run;
%mp_assertdsobs(work.chk1c,
desc=Test1 - the numeric derived value is correct,
test=EQUALS 0,
outds=work.test_results
)
/* proc contents on a view reports a format name without its width, so the
format is compared on the name only */
proc contents noprint data=work.vw_derived
out=work.vw_cols(keep=name label format);
run;
data work.chk1d;
set work.vw_cols;
where name='BAND' and label='Age band' and format=:'$';
run;
%mp_assertdsobs(work.chk1d,
desc=Test1 - the label and default format are applied,
test=EQUALS 1,
outds=work.test_results
)
/**
* Test 2 - a configured format is used as-is
*/
proc sql;
insert into &dclib..mpe_derived set
tx_from=0
,tx_to='31DEC5999:23:59:59'dt
,libref='WORK'
,dsn='CLASS'
,var_nm='FLAG_DT'
,var_deps=''
,var_type='N'
,var_len=8
,var_fmt='DATE9.'
,var_logic='mdy(1,1,2020)'
,active=1;
quit;
%mpe_derived(libds=WORK.CLASS,
dclib=&dclib,
outref=derref2,
outview=work.vw_derived2,
outvars=work.derived_vars2
)
/* asserted on the emitted statement - proc contents on a view does not
report the format in full */
data work.chk2;
infile derref2 truncover;
input line $4000.;
if index(line,'format=DATE9.')>0 then output;
run;
%mp_assertdsobs(work.chk2,
desc=Test2 - the configured format is applied,
test=EQUALS 1,
outds=work.test_results
)
data work.chk2b;
set work.vw_derived2;
if flag_dt ne mdy(1,1,2020) then output;
stop;
run;
%mp_assertdsobs(work.chk2b,
desc=Test2 - the numeric value is correct,
test=EQUALS 0,
outds=work.test_results
)
/**
* Test 3 - no configuration
*/
%mpe_derived(libds=WORK.NOCONFIG,
dclib=&dclib,
outref=derref3,
outview=work.vw_derived3,
outvars=work.derived_vars3
)
%mp_assertdsobs(work.derived_vars3,
desc=Test3 - no derived variables returned,
test=EQUALS 0,
outds=work.test_results
)
%mp_assert(iftrue=(%mf_existds(libds=work.vw_derived3)=0),
desc=Test3 - no view is built when there is no config,
outds=work.test_results
)
data _null_;
infile derref3 end=eof;
input;
if not eof or cats(_infile_) ne '' then do;
putlog 'ERR' 'OR: derived statement file is not empty';
call symputx('emptycheck','0','l');
end;
run;
%mp_assert(iftrue=(%symexist(emptycheck)=0),
desc=Test3 - no statements are generated,
outds=work.test_results
)
/**
* Test 4 - the wrapper scopes the config: only the current, active row of a
* table is used
*/
proc sql;
insert into &dclib..mpe_derived set
tx_from=0
,tx_to='01JAN2020:00:00:00'dt
,libref='WORK'
,dsn='CLASS'
,var_nm='CLOSED_OFF'
,var_deps='AGE'
,var_type='N'
,var_len=8
,var_fmt=''
,var_logic='AGE + 1'
,active=1;
insert into &dclib..mpe_derived set
tx_from=0
,tx_to='31DEC5999:23:59:59'dt
,libref='WORK'
,dsn='CLASS'
,var_nm='NOT_ACTIVE'
,var_deps='AGE'
,var_type='N'
,var_len=8
,var_fmt=''
,var_logic='AGE + 2'
,active=0;
quit;
%mpe_derived(libds=WORK.CLASS,
dclib=&dclib,
outref=derref4,
outview=work.vw_derived4,
outvars=work.derived_vars4
)
data work.chk4;
set work.derived_vars4;
where name in ('CLOSED_OFF','NOT_ACTIVE');
run;
%mp_assertdsobs(work.chk4,
desc=Test4 - a closed version and an inactive row are ignored,
test=EQUALS 0,
outds=work.test_results
)
/* tidy up so repeated runs start clean */
proc sql;
delete from &dclib..mpe_derived where upcase(libref)='WORK';
quit;
%mp_assertscope(COMPARE,ignorelist=_SASJS_WEBOUT_HEADERS,outds=work.test_results)