feat: validation on RLS for REPLACE, + docs + tests. Closes #211
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
# Testing
|
||||
|
||||
Backend (SAS) tests are run with the sasjs CLI from the `sas/` directory.
|
||||
|
||||
## Commands
|
||||
|
||||
- Full deploy + test cycle: `npm run 4gl` (compiles, deploys to the 4gl target, runs makedata) then `sasjs test -t 4gl`.
|
||||
- Run tests only (no rebuild/redeploy): `sasjs test -t 4gl`.
|
||||
- Run a subset of tests: `sasjs test -t 4gl SOMESTRING` — the positional argument filters to matching tests (e.g. `sasjs test -t 4gl stagedata.test.3`).
|
||||
|
||||
## Results
|
||||
|
||||
- Test results: `sas/sasjsresults/testResults.json` / `.csv` / `.xml`
|
||||
- Individual test logs: `sas/sasjsresults/logs/<testsuite>.log` (e.g. `services_editors_stagedata.test.3.log`)
|
||||
- Coverage: `sas/sasjsresults/coverage.lcov`
|
||||
|
||||
## Notes
|
||||
|
||||
- Test source files live in `sas/sasjs/**`; `sas/sasjsbuild/` is generated build output — do not hand-edit it.
|
||||
- `sasjs test` executes tests against the deployed app, so run `npm run 4gl` first after changing any service/hook/test code.
|
||||
- Assertions are made with `%mp_assert()`; results are written to `work.test_results`.
|
||||
@@ -8,6 +8,10 @@ This is different from `.sas` files, where a maximum line length applies. The no
|
||||
|
||||
Rationale: hard-wrapped prose produces noisy diffs when sentences are edited and reflowed, and Markdown renderers already handle wrapping.
|
||||
|
||||
## SAS files
|
||||
|
||||
After creating or modifying any `.sas` files, run `sasjs lint` from the `sas/` directory and ensure the files you touched have no lint warnings (the repo currently has pre-existing warnings in other files, which can be ignored).
|
||||
|
||||
## The .agent folder
|
||||
|
||||
Agent-related content lives in `.agent/`: technical/agent-facing documentation goes in `.agent/docs/` (not `docs/`), and skills in `.agent/skills/`. When writing explanatory or technical docs about the codebase, put them in `.agent/docs/`.
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"fromjs": [
|
||||
{
|
||||
"ADMIN": "AllUsers",
|
||||
"DCPATH": "/opt/data/DataController"
|
||||
"DCPATH": "/opt/data/DataController",
|
||||
"OUTFMT": "JSON"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"fromjs": [
|
||||
{
|
||||
"ADMIN": "AllUsers",
|
||||
"DCPATH": "/tmp/dcdata"
|
||||
"DCPATH": "/tmp/dcdata",
|
||||
"OUTFMT": "JSON"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+10
-4
@@ -7,7 +7,7 @@
|
||||
"name": "dc-sas",
|
||||
"dependencies": {
|
||||
"@sasjs/cli": "4.18.4",
|
||||
"@sasjs/core": "4.68.1"
|
||||
"@sasjs/core": "4.68.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/css-color": {
|
||||
@@ -279,6 +279,12 @@
|
||||
"sasjs": "build/index.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/cli/node_modules/@sasjs/core": {
|
||||
"version": "4.68.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.68.1.tgz",
|
||||
"integrity": "sha512-BjoOzV7h7guCShW4JsFHvB1NNLCKHhPI5708OtEk29itQbgVCMRlEXKFRbnMiuNklktDQDi8dR4XZZ0aK7/4Wg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sasjs/cli/node_modules/@sasjs/utils": {
|
||||
"version": "3.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-3.5.9.tgz",
|
||||
@@ -315,9 +321,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/core": {
|
||||
"version": "4.68.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.68.1.tgz",
|
||||
"integrity": "sha512-BjoOzV7h7guCShW4JsFHvB1NNLCKHhPI5708OtEk29itQbgVCMRlEXKFRbnMiuNklktDQDi8dR4XZZ0aK7/4Wg==",
|
||||
"version": "4.68.2",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.68.2.tgz",
|
||||
"integrity": "sha512-flbx36sNN7PvEIjX8H6UuGIKMQ8Ra3ZHEze9O66Wtk6cU+T0RV1i199LYXVumkja/VXSW334HlNpiSVR4o9qqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sasjs/lint": {
|
||||
|
||||
+1
-1
@@ -29,6 +29,6 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@sasjs/cli": "4.18.4",
|
||||
"@sasjs/core": "4.68.1"
|
||||
"@sasjs/core": "4.68.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
@brief Testing mpe_refreshtables macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mpe_refreshlibs.sas
|
||||
@li mpe_refreshtables.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_mkdir.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
@@ -14,14 +17,32 @@
|
||||
|
||||
**/
|
||||
|
||||
/* create a new library with a demo format catalog, so it always exists */
|
||||
%let reftstloc=%sysfunc(pathname(&dc_libref))/reftst%mf_getuniquefileref();
|
||||
%mf_mkdir(&reftstloc)
|
||||
libname reftst "&reftstloc";
|
||||
|
||||
data work.reftstfmts;
|
||||
length fmtname $32;
|
||||
fmtname='REFFMT';
|
||||
do start=1 to 10;
|
||||
label=cats('Format ',start);
|
||||
output;
|
||||
end;
|
||||
run;
|
||||
proc format cntlin=work.reftstfmts library=reftst.dcfmts;
|
||||
run;
|
||||
|
||||
/* register the library in mpe_datacatalog_libs */
|
||||
%mpe_refreshlibs(lib=REFTST)
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mpe_refreshtables(FMTONLY)
|
||||
%mpe_refreshtables(REFTST)
|
||||
%mp_assertscope(COMPARE,
|
||||
desc=Checking macro variables against previous snapshot
|
||||
)
|
||||
|
||||
/* make sure that the process picks up a library that contains only a single
|
||||
catalog */
|
||||
/* make sure that the process picks up a library that contains a catalog */
|
||||
proc sql;
|
||||
create table work.libinfo as
|
||||
select a.engine,
|
||||
@@ -37,7 +58,7 @@ create table work.libinfo as
|
||||
from &mpelib..mpe_datacatalog_libs(where=(&dc_dttmtfmt. lt tx_to)) a
|
||||
left join &mpelib..mpe_datastatus_libs(where=(&dc_dttmtfmt. lt tx_to)) b
|
||||
on a.libref=b.libref
|
||||
where a.libref="FMTONLY";
|
||||
where a.libref="REFTST";
|
||||
|
||||
%let test1=0;
|
||||
data _null_;
|
||||
@@ -46,5 +67,5 @@ data _null_;
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(&test1>0),
|
||||
desc=Checking fmtonly.dcfmts was picked up
|
||||
desc=Checking reftst.dcfmts was picked up
|
||||
)
|
||||
@@ -105,7 +105,7 @@ run;
|
||||
desc=Checking leading blanks were applied
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(&leadcheck=1),
|
||||
iftrue=(&speshcheck=1),
|
||||
desc=Checking special characters were applied
|
||||
)
|
||||
|
||||
|
||||
@@ -72,14 +72,16 @@ run;
|
||||
mdebug=&sasjs_mdebug
|
||||
)
|
||||
|
||||
/* search terms may span multiple lines, so track matches across records */
|
||||
%let abort1=0;
|
||||
data _null_;
|
||||
retain foundabort foundmsg 0;
|
||||
infile wb1;
|
||||
input;
|
||||
putlog _infile_;
|
||||
if index(_infile_,'sasjsAbort')
|
||||
and index(_infile_,'REPLACE loadtype')
|
||||
then call symputx('abort1',1);
|
||||
if index(_infile_,'sasjsAbort') then foundabort=1;
|
||||
if index(_infile_,'REPLACE loadtype') then foundmsg=1;
|
||||
if foundabort=1 and foundmsg=1 then call symputx('abort1',1);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
@@ -140,12 +142,13 @@ run;
|
||||
|
||||
%let abort2=0;
|
||||
data _null_;
|
||||
retain foundabort foundmsg 0;
|
||||
infile wb2;
|
||||
input;
|
||||
putlog _infile_;
|
||||
if index(_infile_,'sasjsAbort')
|
||||
and index(_infile_,'row level EDIT security')
|
||||
then call symputx('abort2',1);
|
||||
if index(_infile_,'sasjsAbort') then foundabort=1;
|
||||
if index(_infile_,'EDIT security') then foundmsg=1;
|
||||
if foundabort=1 and foundmsg=1 then call symputx('abort2',1);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
|
||||
@@ -50,8 +50,8 @@ run;
|
||||
%mp_abort(iftrue=(&badloadtypes ne 0)
|
||||
,mac=mpe_row_level_security_postedit
|
||||
,msg=%str(Row level EDIT security cannot be applied to table(s) with a%trim(
|
||||
) REPLACE loadtype, eg: &badloadtype. Remove the security rule(s) or %trim(
|
||||
)change the loadtype.)
|
||||
) REPLACE loadtype, eg: &badloadtype. Remove the security rule(s) or%trim(
|
||||
) change the loadtype.)
|
||||
)
|
||||
|
||||
/* ignore scope and group for validation */
|
||||
|
||||
@@ -91,8 +91,8 @@ run;
|
||||
|
||||
%mp_abort(iftrue=(&badloadtypes ne 0)
|
||||
,mac=mpe_tables_postedit
|
||||
,msg=%str(REPLACE loadtype cannot be applied to table(s) with row level %trim(
|
||||
)EDIT security, eg: &badloadtype. Remove the security rule(s) or change%trim(
|
||||
,msg=%str(REPLACE loadtype cannot be applied to table(s) with row level%trim(
|
||||
) EDIT security, eg: &badloadtype. Remove the security rule(s) or change%trim(
|
||||
) the loadtype.)
|
||||
)
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
|
||||
**/
|
||||
|
||||
%global path ADMIN ;
|
||||
%global path ADMIN OUTFMT;
|
||||
|
||||
/* response format - HTML (default) or JSON */
|
||||
%let OUTFMT=HTML;
|
||||
|
||||
%webout(FETCH)
|
||||
|
||||
@@ -39,6 +42,19 @@ data _null_;
|
||||
call symputx('ADMIN',ADMIN);
|
||||
run;
|
||||
|
||||
/* check if a JSON response was requested (OUTFMT column in fromjs) */
|
||||
%if &exist=1 %then %do;
|
||||
data _null_;
|
||||
dsid=open('work.fromjs');
|
||||
if varnum(dsid,'OUTFMT')>0 then do;
|
||||
set work.fromjs;
|
||||
call symputx('OUTFMT',upcase(outfmt));
|
||||
end;
|
||||
rc=close(dsid);
|
||||
stop;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&_program
|
||||
,msg=%str(Issue on makedata entry)
|
||||
@@ -140,6 +156,20 @@ run;
|
||||
)
|
||||
|
||||
|
||||
%if &OUTFMT=JSON %then %do;
|
||||
data work.makedata_response;
|
||||
length STATUS $10 MESSAGE $256 DCPATH $256 LIBREF $8 ADMIN $64;
|
||||
STATUS='SUCCESS';
|
||||
MESSAGE="Data Controller Config created by &sysuserid";
|
||||
DCPATH="&dcpath";
|
||||
LIBREF="&DC_LIBREF";
|
||||
ADMIN="&admin";
|
||||
run;
|
||||
%webout(OPEN)
|
||||
%webout(OBJ, makedata_response)
|
||||
%webout(CLOSE)
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
file _webout;
|
||||
put '<h3>Data Controller Config</h3>';
|
||||
@@ -159,6 +189,7 @@ data _null_;
|
||||
put "<li><a target='_blank' href='/AppStream/DataController'>Launch!</a>";
|
||||
put "</li></ol>";
|
||||
run;
|
||||
%end;
|
||||
|
||||
/* We ran successfully, now remove configurator and makedata STPs */
|
||||
%ms_deletefile(&root/services/admin/configurator.sas)
|
||||
|
||||
Reference in New Issue
Block a user