Files
dc/.agents/skills/dc-estate-testing/SKILL.md
T
dc eeffa92f9e
Build / Build-and-ng-test (pull_request) Successful in 6m57s
Lighthouse Checks / lighthouse (pull_request) Successful in 20m54s
Build / Build-and-test-development (pull_request) Successful in 34m49s
fix(makedata): name the sasjs server group in the estate seed
makeDataServer.json seeded ADMIN=AllUsers, a Viya-style group that no account on
the shared estate belongs to. The seed still reports SUCCESS with a group the
caller is not in, and then every getdata/stagedata call aborts with
'<user> is not authorised to edit <libds> in the MPE_SECURITY table', which
reads like a broken deployment. The estate's accounts are in all-users, the
SASjs Server group every logged-in user has, so seed with that and describe the
rule in the estate-testing skill rather than telling the reader to override the
shipped value.
2026-10-28 22:48:02 +00:00

8.3 KiB

name, description
name description
dc-estate-testing Use when deploying Data Controller to a shared SASjs Server estate (sas.4gl.io) or running the SAS test suite against it. Covers the target and credential setup, the makedata seed the suite needs on a fresh appLoc and the group it must name, how to read the results, and how to deploy the frontend so the streamed app is the build you just made.

Deploying and testing on the shared SASjs Server estate

The whole cycle is two npm scripts plus sasjs test. Everything below explains what those scripts do and the steps that go wrong.

cd sas && npm ci                             # macro dependencies
cd ../client && npm ci && npm run build      # frontend only - see "Deploying the frontend"
cd ../sas && npm run server                  # cpfavicon + sasjs cbd -t server + makedata
npx sasjs test -t server

npm run server is sasjs cbd -t server && npm run serverdata, and the seed in the second half is not optional. sasjs cbd on its own deploys the services and the test programs but leaves the appLoc with no Data Controller configuration, and testsetup reads <appLoc>/services/public/settings.sas through the drive API. makedata is what creates that file, so without the seed testsetup gets a 404, aborts, and 59 of the 64 test programs fail to complete on setup alone - which reads like a broken deployment rather than a missing seed.

The targets

server is the shared estate: serverType: SASJS on https://sas.4gl.io, appLoc /Public/app/dc, a real SAS 9 binary behind SASjs Server (GET /SASjsApi/info reports mode: server, runTimes: ["sas"], localLoginEnabled: true). 4gl is the same estate with a slightly different config, hence the older npm run 4gl / 4glmakedata pair.

A run against server exercises the SASjs Server branch of every macro and the SAS 9 backend, and nothing else: no Viya branch, no SAS 9 STP branch. Say that when reporting, rather than implying the matrix is covered.

To deploy your own branch without replacing the app everyone else uses, add a target that copies server and changes appLoc and streamServiceName (for example /Public/app/<you>/dc), keep it out of the commit, and pass it with -t. sasjs cbd overwrites the whole appLoc it is pointed at.

Credentials

The CLI logs in with a registered client, not a username and password: .env.<target> (gitignored, mode 600) needs CLIENT, ACCESS_TOKEN and REFRESH_TOKEN. With CLIENT alone it prints a /SASjsLogon?client_id=... URL and waits for an authorization code, which cannot be answered headless.

Mint the pair from a local login, with the password read from a file rather than argv:

  1. GET / with a cookie jar - it sets XSRF-TOKEN, which every POST must echo in an X-XSRF-TOKEN header.
  2. POST /SASLogon/login with {"username","password"}.
  3. GET /SASjsApi/client on the same session - the registered client ids.
  4. POST /SASLogon/authorize with {"clientId"} -> {"code"}.
  5. POST /SASjsApi/auth/token with {"clientId","code"} -> accessToken / refreshToken.

An unmatched /SASjsApi/... path answers 200 with the Swagger page, so check the content type; HTML means the route does not exist. Delete the token file when the run is over.

The makedata seed

npm run serverdata sends deploy/makeDataServer.json: one fromjs row with ADMIN, DCPATH and OUTFMT. Two things about it are worth knowing before you trust its result.

  • ADMIN must name a group the calling account is actually in. The shipped value is all-users, the SASjs Server group that every logged-in user is a member of, which is what the shared estate needs. A name the account is not in - a Viya-style group such as AllUsers, or one estate's own group carried into another - still answers SUCCESS from the seed, and then every later getdata/stagedata call aborts with <user> is not authorised to edit <lib>.<ds> in the <lib>.MPE_SECURITY table. Check what the account belongs to with a call to services/public/getgroups before changing the value.
  • Each run creates a new library. The response carries STATUS, DCPATH and LIBREF (for example DC621835), and the LIBREF is clock-derived, so a libref from an earlier run answers dataset <lib>.<table> does not exist - take the current one from the response.
  • KEEPME controls whether the seed can be repeated. Without it makedata deletes itself and the configurator after a successful run (deliberately, so it cannot be executed twice over real data), and a second seed fails with The Program at (<appLoc>/services/admin/makedata) does not exist until you run sasjs cbd again. A harness that reseeds between runs sets KEEPME in the input row.
  • Seed once per deploy, not before every test run. The suite builds its own dctest library from the seeded control tables; reseeding in between is wasted work, and there is no need to run makedata and sasjs test in the opposite order.

Reading the results

Results land in sas/sasjsresults/: testResults.json (the counts worth quoting: tests_with_results, tests_that_pass, failed_to_complete, completed_with_failures), testResults.csv (one row per assertion, lower-case headers test_target, test_loc, sasjs_test_id, test_suite_result, test_description, test_url), and one SAS log per program under logs/. The CLI's closing lines are not the source of truth when they disagree with the JSON.

A current green run is 64 of 64 test programs with 241 assertions passing and nothing failing to complete.

ERROR lines in the per-program logs are expected and are not a failed run: the specs that assert a refusal get ERROR: Execution canceled by an %ABORT CANCEL statement, and the ones that feed bad payloads get the rejection message they are asserting on (Invalid libds:..., Invalid libref:...). Judge the run by the JSON counts, then by whether the errors you find are the ones the tests asked for.

Deploying the frontend

The streamed web app is a separate artefact and sasjs cbd only streams it if client/dist exists:

  1. client/libraries/sheet-crypto.tgz.gpg is an encrypted dependency (file:libraries/sheet-crypto.tgz in client/package.json) that is not on the public registry. Decrypt it with the passphrase the maintainer holds: printf '%s' "$SHEET_PWD" | gpg --batch --yes --passphrase-fd 0 --output ./libraries/sheet-crypto.tgz --decrypt ./libraries/sheet-crypto.tgz.gpg.
  2. client/.npmrc is gitignored and needs legacy-peer-deps=true (the committed lock is written with it) plus the @sheet registry lines.
  3. cd client && npm ci && npm run build.
  4. The build carries the repo default appLoc, so point it at the deployment before deploying: rewrite apploc="/Public/app/dc" in dist/index.html to the target appLoc.
  5. cd ../sas && npx sasjs cbd -t <target> - with client/dist present the log says Copying web app files and Web app is available at <url>.

Without step 1 the deploy prints ERROR An error has occurred when compiling web app services ... webSourcePath: .../client/dist doesn't exist and then deploys the services anyway, so the deploy looks successful while the app streams nothing. The AppStream URL is session-protected: an anonymous curl gets a 401, so verify it with a logged-in cookie jar and fetch the main.<hash>.js the page names.

Never commit client/.npmrc or the decrypted tarball; both are ignored, and git status staying clean is the check.

Reading a failed run

  • ERROR An error occurred while executing the request. Job location: <appLoc>/tests/... means the response was not JSON, so the program broke before it could write results. Open sasjsresults/logs/<suite>.log and read the first ERROR; a single compile break cascades into hundreds of ERROR 180-322: Statement is not valid lines and the tail is noise.
  • WARNING: Apparent symbolic reference TESTLOC not resolved in testsetup.log, next to a 404 Not Found for services/public/settings.sas, is the missing seed described above, not a macro problem.
  • sasjs test runs the DEPLOYED program, so after editing a service, hook or test, run sasjs cbd again before testing or you will debug code that is no longer in the tree.
  • A deployment can succeed and still be missing its web app, its data or its configuration - check the thing you changed rather than the exit code.