Files
dc/sas/sasjs/tests/testsetup.sas
T
dc 16e9b31e65
Build / Build-and-ng-test (pull_request) Successful in 4m50s
Build / Build-and-test-development (pull_request) Failing after 5m48s
Lighthouse Checks / lighthouse (pull_request) Failing after 2m53s
test(harness): a measurable wrapper around mx_execute for service tests
Tests inferred whether a service call worked by reading an outlib that a failed
call never touched, so assertions ran against the previous call's tables.  This
adds dc_execute, which wraps mx_execute and makes each call observable:

 - failfast: abort as soon as the child did not complete, with its message, so
   the first failure is the real one
 - a status row per call (outstatus) so a test can assert the outcome
 - the child's log behind the harness debug flag, quiet otherwise

testsetup reseeds the data baseline before the test library is built, makedata
honours a keepme switch and getdata initialises the variables it later tests
with %length, plus desc= values reworded where a comma or apostrophe unbalanced
the parameter.

sasjs/macros/mx_execute.sas is a TEMPORARY copy from sasjs/core#476, which adds
outlogds/outstatus and stops leaving work.results in the caller WORK.  Delete it
and bump the dependency once that PR is released.
2026-10-26 23:29:46 +00:00

324 lines
8.2 KiB
SAS

/**
@file
@brief test setup
<h4> SAS Macros </h4>
@li dc_getsettings.sas
@li mf_getapploc.sas
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_mkdir.sas
@li mm_createstp.sas
@li mm_createlibrary.sas
@li mm_deletelibrary.sas
@li mm_deletestp.sas
@li mm_getstpcode.sas
@li mp_assert.sas
@li mddl_dc_difftable.sas
@li mddl_dc_locktable.sas
@li mp_init.sas
@li ms_getgroups.sas
@li mx_execute.sas
@li ms_createfile.sas
@li ms_deletefile.sas
@li mx_getcode.sas
**/
%mp_init()
%global apploc _program;
%let defaultcontext=Compute Reusable;
data _null_;
length _pgm $1000;
_pgm=symget('_program');
if _pgm='' then do;
if "&SYSPROCESSMODE" = 'SAS Workspace Server'
then apploc=
'/30.SASApps/3030.Projects/303001.DataController/build2/DataController';
else apploc='/Public/app/viya';
call symputx('apploc',apploc);
call symputx('_program',cats(apploc,'/tests/services/bottom'));
end;
else do;
cnt=find(_pgm,'/tests/');
if cnt=0 then cnt=find(_pgm,'/services/');
if cnt=0 then cnt=find(_pgm,'/jobs/');
apploc=substr(_pgm,1,cnt-1);
put cnt= apploc=;
call symputx('apploc',apploc);
end;
run;
%dc_getsettings()
/**
* Reseed before anything below derives a path or registers a table.
*
* makedata creates a fresh DC library, seeds the control tables and rewrites
* settings.sas, so every run starts from the same state - and so the test
* library built further down belongs to the library the tests will use
* (build it first and the suite runs against a new library that has never
* heard of DCTEST).
*
* The admin group is taken from the runner's own group list rather than from
* the existing settings: on SASjs Server group names are lowercase slugs that
* change with a server upgrade, and a settings file naming a stale group
* silently removes everyone's admin rights.
*/
%macro dc_testbaseline();
%local root baselinegrp baselinepath fromjsref;
%if %mf_getplatform() ne SASJS %then %return;
%let root=%mf_getapploc(&_program);
/* ms_getgroups is the server's group API - the macro underneath
* dc_getusergroups, without the metadata branch a SASjs Server build cannot
* resolve. These are the names mpe_accesscheck compares against. */
%ms_getgroups(user=%mf_getuser(),outds=work.dcgroups)
data _null_;
set work.dcgroups;
if _n_=1 then call symputx('baselinegrp',name,'l');
stop;
run;
data _null_;
length p part base $200;
p=pathname("&dc_libref");
base='/';
do i=1 to countw(p,'/')-1;
part=scan(p,i,'/');
if part ne '' then base=cats(base,part,'/');
end;
if substr(base,length(base),1)='/' then base=substr(base,1,length(base)-1);
call symputx('baselinepath',base,'l');
run;
%put NOTE: testsetup baseline admin=&baselinegrp path=&baselinepath;
%if %length(&baselinegrp)=0 or %length(&baselinepath)=0 %then %return;
%let fromjsref=%mf_getuniquefileref();
data _null_;
file &fromjsref termstr=crlf;
put 'ADMIN:$char9. DCPATH:$char20. OUTFMT:$char4. KEEPME:$char1.';
put "&baselinegrp,&baselinepath,JSON,Y";
run;
/* no outlib: makedata answers with its own JSON/HTML, and a two level name
* is not a valid libref */
%mx_execute(&root/services/admin/makedata,
viyacontext=&defaultcontext,
inputfiles=&fromjsref:fromjs
)
/* reload - makedata has just rewritten settings.sas with the new libref */
%dc_getsettings()
%put NOTE: testsetup baseline libref=&dc_libref admin=&dc_admin_group;
%mend dc_testbaseline;
%dc_testbaseline();
/**
* Set up Test library
*/
%let testloc=%sysfunc(pathname(&DC_LIBREF))/fmt%mf_getuniquefileref();
%mf_mkdir(&testloc)
libname dctest "&testloc";
data dctest.mpe_x_test;
set &DC_LIBREF..mpe_x_test;
run;
data dctest.wideboy;
/* Create 500 character variables and 500 numeric variables */
array char_fields[500] $20 char1-char500;
array num_fields[500] num1-num500;
length alphabet $62;
alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
/* Set a seed for reproducible random data */
call streaminit(12345);
do row_id = 1 to 1000;
/* Generate random 20-character alphanumeric values */
do field = 1 to dim(char_fields);
do position = 1 to 20;
substr(char_fields[field], position, 1) =
substr(alphabet,
floor(rand('uniform') * length(alphabet)) + 1,
1
);
end;
end;
/* Generate random numeric values from a normal distribution */
do field = 1 to dim(num_fields);
num_fields[field] = ceil(rand('normal', 0, 1));
end;
output;
end;
stop;
run;
/* test library with only one format catalog */
%mf_mkdir(&testloc/fmtonly)
libname fmtonly "&testloc/fmtonly";
/* add formats */
PROC FORMAT library=dctest.dcfmts;
picture MyMSdt other='%0Y-%0m-%0dT%0H:%0M:%0S' (datatype=datetime);
RUN;
data work.fmts;
length fmtname $32;
do fmtname='SMAUG','MORDOR','GOLLUM';
do start=1 to 10;
label= cats('Dragon ',start);
output;
end;
end;
run;
proc sort data=work.fmts nodupkey;
by fmtname;
run;
proc format cntlin=work.fmts library=dctest.dcfmts;
run;
proc format library=dctest.dcfmts;
invalue indays (default=13) other=42;
run;
proc sql;
delete from &DC_LIBREF..mpe_tables where libref="DCTEST";
data work.append;
if 0 then set &DC_LIBREF..mpe_tables;
tx_from=0;
tx_to='31DEC9999:23:59:59'dt;
libref="DCTEST";
dsn='DCFMTS-FC';
buskey='TYPE FMTNAME FMTROW';
loadtype='FORMAT_CAT';
num_of_approvals_required=1;
output;
dsn='WIDEBOY';
buskey='ROW_ID CHAR1 NUM1';
loadtype='UPDATE';
output;
stop;
run;
proc append base=&dc_libref..mpe_tables data=work.append;
run;
/* lone format catalog */
proc format cntlin=work.fmts library=fmtonly.dcfmts;
run;
/* add some other tables */
%mddl_dc_locktable(libds=dctest.locktable)
%mddl_dc_difftable(libds=dctest.difftable)
/**
* Update settings service
*/
%let root=%mf_getapploc(&_program);
%put &=root;
filename setngs temp;
options mprint;
%macro update_settings();
%if "&_debug"="2477" or "&_debug"="fields,log,trace" %then %do;
%put debug mode activated;
options mprint;
%put _global_;
%end;
%let pf=%mf_getplatform();
%if &pf=SASVIYA %then %do;
filename jobout filesrvc folderpath="&root/services";
data _null_;
file setngs;
infile jobout('settings.sas') end=last;
input;
put _infile_;
if last then do;
put "libname dctest '&testloc';";
put "libname fmtonly '&testloc/fmtonly';";
end;
run;
data _null_;
file jobout('settings.sas');
infile setngs;
input;
put _infile_;
putlog _infile_;
run;
%end;
%else %if &pf=SASJS %then %do;
%mx_getcode(&root/services/public/settings,outref=stngs)
filename sendback "&_SASJS_WORK/settings2.txt" lrecl=1000;
data _null_;
file sendback;
infile stngs end=last lrecl=1000;
input;
put _infile_;
if last then do;
put "libname dctest '&testloc';";
put "libname fmtonly '&testloc/fmtonly';";
end;
run;
%ms_deletefile(&root/services/public/settings.sas)
%ms_createfile(&root/services/public/settings.sas, inref=sendback,mdebug=1)
%end;
%else %do;
%let libname=DC Test;
%mm_deletelibrary(name=&libname)
%mm_createlibrary(
libname=&libname
,libref=dctest
,libdesc=Testing the DC
,engine=BASE
,tree=/30.SASApps/3030.Projects/303001.DataController
,servercontext=SASApp
,directory=&testloc
,mDebug=1
)
%mm_getstpcode(tree=&root/services/public
,name=Data_Controller_Settings
,outloc=&_SASJS_WORK/settings.txt
)
data _null_;
file "&_SASJS_WORK/settings2.txt";
infile "&_SASJS_WORK/settings.txt" end=last;
input;
put _infile_;
if last then do;
put "libname dctest '&testloc';";
put "libname fmtonly '&testloc/fmtonly';";
end;
run;
%mm_deletestp(target=&root/services/public/Data_Controller_Settings)
%mm_createstp(stpname=Data_Controller_Settings
,filename=settings2.txt
,directory=&_SASJS_WORK
,tree=&root/services/public
,Server=SASApp
,stptype=2
,mdebug=1
,stpdesc=Data Controller Configuration
,minify=NO
)
%end;
%mend update_settings;
%update_settings()
%mp_assert(
iftrue=(&syscc=0),
desc=Makedata ran without errors,
outds=work.test_results
)