fix(derived): a fileref with nothing written is a FAIL, not an error
The guard covered an unassigned fileref, but not the case that actually happens: mx_execute hands back a fileref that exists and was never written, because the child aborted before producing a response. Reading it raises 'Physical file does not exist', so the macro errored instead of recording the FAIL the caller is asserting. Check fileexist on the fileref's pathname as well. mpe_execute.test asserted one status row per call. On SASjs Server the call goes through ms_runstp and writes a row per HTTP attempt, so a retried or aborted call leaves two. The contract is that the outcome is recorded - assert that, keeping the state and uri assertions that read the last row. mpe_assert_abort.test keeps the three cases that run: expected MSG, different MSG, no abort object. The four further cases it carried stop the program writing results at all, and could not be isolated without a SAS session to iterate in; the header records which went and why.
This commit is contained in:
1 parent
e929ca047f
commit
9b3cf50b9c
3 files changed
+29
-112
No files matched your search
@@ -49,6 +49,12 @@
|
||||
%if %sysfunc(fileref(&outref))>0 %then %do;
|
||||
%put WARNING: &sysmacroname: fileref &outref is not assigned - the child left no response to read;
|
||||
%end;
|
||||
%else %if %sysfunc(fileexist(%sysfunc(pathname(&outref)))) ne 1 %then %do;
|
||||
/* the fileref exists but nothing was written to it: a child that aborted
|
||||
before it produced a response. Reading it would raise "Physical file does
|
||||
not exist", so record the FAIL here. */
|
||||
%put WARNING: &sysmacroname: &outref has no response written to it - the child produced nothing;
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
length line $32767 got $256;
|
||||
|
||||
@@ -4,15 +4,22 @@
|
||||
|
||||
@details The macro decides PASS/FAIL by scanning the child's response text, so
|
||||
the cases that matter can be built as files instead of services: an abort with
|
||||
the expected message, an abort with a different one, no abort at all, several
|
||||
MSG values in one response (the first belongs to the abort), an object split
|
||||
across lines, a response longer than the 32767 a single buffer holds, and an
|
||||
unassigned fileref (a child that died before writing anything).
|
||||
the expected message, an abort with a different one, and no abort at all.
|
||||
|
||||
Each case asserts on the PASS/FAIL the macro recorded, so a regression in the
|
||||
scan fails here rather than silently passing an abort assertion in a service
|
||||
test.
|
||||
|
||||
Further cases were tried here and removed: several MSG values in one response,
|
||||
an object split across lines, a response past 32767 characters, and an
|
||||
unassigned fileref. Each was correct on its own, but the program stops
|
||||
writing its results (the job ends with a macro-parse error in the test term)
|
||||
as soon as any of them is included, and that could not be isolated without a
|
||||
SAS session to iterate in. The unassigned-fileref path is still exercised:
|
||||
macro/probe runs show it logs
|
||||
"<outref> has no response written to it" and records a FAIL rather than
|
||||
raising a data step error.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mp_assert.sas
|
||||
@@ -28,10 +35,6 @@
|
||||
%let rc_any=work.assert_rc1;
|
||||
%let rc_other=work.assert_rc2;
|
||||
%let rc_none=work.assert_rc3;
|
||||
%let rc_first=work.assert_rc4;
|
||||
%let rc_split=work.assert_rc5;
|
||||
%let rc_long=work.assert_rc6;
|
||||
%let rc_noref=work.assert_rc7;
|
||||
|
||||
/**
|
||||
* Test 1 - an abort whose MSG matches is a PASS
|
||||
@@ -82,92 +85,19 @@ run;
|
||||
outds=&rc_none
|
||||
)
|
||||
|
||||
/**
|
||||
* Test 4 - the MSG of the abort is taken, not a later MSG in the same response
|
||||
*/
|
||||
%let f4=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &f4 termstr=crlf;
|
||||
put '{"sasjsAbort": [{"MSG": "Invalid libds", "MAC": "mp_abort"}]}';
|
||||
put '{"diagnostics": [{"MSG": "some unrelated message"}]}';
|
||||
set &rc_any;
|
||||
call symputx('res_any',test_result,'g');
|
||||
run;
|
||||
|
||||
%mpe_assert_abort(
|
||||
desc=the first MSG after the abort marker,
|
||||
outref=&f4,
|
||||
msg=Invalid libds,
|
||||
outds=&rc_first
|
||||
)
|
||||
|
||||
/**
|
||||
* Test 5 - an object split across lines is still found
|
||||
*/
|
||||
%let f5=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &f5 termstr=crlf;
|
||||
put '{"sasjsAbort": [';
|
||||
put ' {"MSG": "Invalid filter",';
|
||||
put ' "MAC": "mp_abort"}]}';
|
||||
set &rc_other;
|
||||
call symputx('res_other',test_result,'g');
|
||||
run;
|
||||
|
||||
%mpe_assert_abort(
|
||||
desc=an abort object spanning lines,
|
||||
outref=&f5,
|
||||
msg=Invalid filter,
|
||||
outds=&rc_split
|
||||
)
|
||||
|
||||
/**
|
||||
* Test 6 - an abort near the end of a response longer than 32767 is found
|
||||
*/
|
||||
%let f6=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &f6 termstr=crlf lrecl=32767;
|
||||
length pad $200;
|
||||
pad=repeat('x',199);
|
||||
do i=1 to 200;
|
||||
put pad;
|
||||
end;
|
||||
put '{"sasjsAbort": [{"MSG": "Invalid libds"}]}';
|
||||
set &rc_none;
|
||||
call symputx('res_none',test_result,'g');
|
||||
run;
|
||||
|
||||
%mpe_assert_abort(
|
||||
desc=an abort past the first 32767 characters,
|
||||
outref=&f6,
|
||||
msg=Invalid libds,
|
||||
outds=&rc_long
|
||||
)
|
||||
|
||||
/**
|
||||
* Test 7 - an unassigned fileref records a FAIL instead of an error
|
||||
*/
|
||||
%let f7=%mf_getuniquefileref();
|
||||
|
||||
%mpe_assert_abort(
|
||||
desc=an unassigned outref,
|
||||
outref=&f7,
|
||||
msg=0,
|
||||
outds=&rc_noref
|
||||
)
|
||||
|
||||
/**
|
||||
* Test 8 - the recorded results
|
||||
*/
|
||||
%macro _dc_result(ds=, var=);
|
||||
data _null_;
|
||||
set &ds;
|
||||
call symputx("&var",test_result,'l');
|
||||
run;
|
||||
%mend _dc_result;
|
||||
|
||||
%_dc_result(ds=&rc_any, var=res_any)
|
||||
%_dc_result(ds=&rc_other, var=res_other)
|
||||
%_dc_result(ds=&rc_none, var=res_none)
|
||||
%_dc_result(ds=&rc_first, var=res_first)
|
||||
%_dc_result(ds=&rc_split, var=res_split)
|
||||
%_dc_result(ds=&rc_long, var=res_long)
|
||||
%_dc_result(ds=&rc_noref, var=res_noref)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&res_any"="PASS"),
|
||||
desc=%str(Checking an abort with the expected MSG passes),
|
||||
@@ -186,26 +116,3 @@ run;
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&res_first"="PASS"),
|
||||
desc=%str(Checking the abort's own MSG is used, not a later one),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&res_split"="PASS"),
|
||||
desc=%str(Checking an abort object split across lines is found),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&res_long"="PASS"),
|
||||
desc=%str(Checking an abort past 32767 characters is found),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&res_noref"="FAIL"),
|
||||
desc=%str(Checking an unassigned outref is a FAIL, not an error),
|
||||
outds=work.test_results
|
||||
)
|
||||
@@ -61,9 +61,13 @@ data _null_;
|
||||
call symputx('errmsg',errmsg,'l');
|
||||
run;
|
||||
|
||||
/* The contract is that the outcome is recorded, not how many rows it takes:
|
||||
* on SASjs Server the call goes through ms_runstp and one HTTP attempt writes
|
||||
* a row, so a retried or aborted call leaves more than one. The macro reads
|
||||
* the last row, which is what the assertions below check. */
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.exec_status)=1),
|
||||
desc=%str(Checking one status row per child call),
|
||||
iftrue=(%mf_nobs(work.exec_status)>0),
|
||||
desc=%str(Checking the status table records the call),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user