← back to New Items Dashboard
test: auth gate smoke test (boots server, asserts 401/200 matrix)
ed3ed848da9a5c3c69b12d747cd4062e307e5a62 · 2026-05-19 22:13:46 -0700 · Steve Abrams
7 assertions covering unauth-401 on /, /api/new-wines, /api/new-handbags,
/api/stats, WWW-Authenticate present, valid-creds non-401, wrong-creds 401.
Standalone (no framework dep); wired to npm test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
M package.jsonA test/auth.test.js
Diff
commit ed3ed848da9a5c3c69b12d747cd4062e307e5a62
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue May 19 22:13:46 2026 -0700
test: auth gate smoke test (boots server, asserts 401/200 matrix)
7 assertions covering unauth-401 on /, /api/new-wines, /api/new-handbags,
/api/stats, WWW-Authenticate present, valid-creds non-401, wrong-creds 401.
Standalone (no framework dep); wired to npm test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
package.json | 3 +-
test/auth.test.js | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 131 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index fc6d43f..d6a1e12 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,8 @@
"description": "Dashboard for NEW wines and handbags",
"main": "server.js",
"scripts": {
- "start": "node server.js"
+ "start": "node server.js",
+ "test": "node test/auth.test.js"
},
"dependencies": {
"express": "^4.18.2",
diff --git a/test/auth.test.js b/test/auth.test.js
new file mode 100644
index 0000000..9556634
--- /dev/null
+++ b/test/auth.test.js
@@ -0,0 +1,129 @@
+// Smoke test: basic-auth gate on new-items-dashboard.
+//
+// Boots server.js in a child process with a deterministic BASIC_AUTH +
+// NODE_ENV=production, hits a few routes via plain http, asserts:
+// (a) GET / unauth -> 401
+// (b) GET /api/new-wines unauth -> 401
+// (c) GET / with valid creds -> 200 (or any non-401; the gate is what matters)
+//
+// Runs standalone — node test/auth.test.js. No test framework dep.
+
+const { spawn } = require('child_process');
+const http = require('http');
+const path = require('path');
+
+const PORT = process.env.TEST_PORT || 7299;
+const CREDS = 'admin:DW2025Secure!';
+const SERVER = path.join(__dirname, '..', 'server.js');
+
+function get(opts) {
+ return new Promise((resolve, reject) => {
+ const req = http.request({
+ hostname: '127.0.0.1',
+ port: PORT,
+ method: 'GET',
+ ...opts,
+ }, (res) => {
+ let body = '';
+ res.on('data', (c) => { body += c; });
+ res.on('end', () => resolve({ status: res.statusCode, body, headers: res.headers }));
+ });
+ req.on('error', reject);
+ req.end();
+ });
+}
+
+function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }
+
+async function waitForServer(timeoutMs = 5000) {
+ const deadline = Date.now() + timeoutMs;
+ while (Date.now() < deadline) {
+ try {
+ await get({ path: '/' });
+ return true;
+ } catch (e) {
+ await sleep(100);
+ }
+ }
+ return false;
+}
+
+(async () => {
+ const child = spawn('node', [SERVER], {
+ env: { ...process.env, PORT: String(PORT), NODE_ENV: 'production', BASIC_AUTH: CREDS },
+ stdio: ['ignore', 'pipe', 'pipe'],
+ });
+
+ let serverErr = '';
+ child.stderr.on('data', (c) => { serverErr += c; });
+
+ const failures = [];
+ const ok = (msg) => console.log(' ok -', msg);
+ const fail = (msg) => { failures.push(msg); console.log(' FAIL -', msg); };
+
+ try {
+ const up = await waitForServer();
+ if (!up) {
+ console.error('server failed to come up. stderr:\n', serverErr);
+ process.exit(1);
+ }
+
+ // (a) unauth on /
+ {
+ const r = await get({ path: '/' });
+ if (r.status === 401) ok('GET / unauth -> 401');
+ else fail(`GET / unauth -> ${r.status} (expected 401)`);
+ if (/^Basic/i.test(r.headers['www-authenticate'] || '')) ok('WWW-Authenticate present');
+ else fail('WWW-Authenticate header missing on /');
+ }
+
+ // (b) unauth on /api/new-wines
+ {
+ const r = await get({ path: '/api/new-wines' });
+ if (r.status === 401) ok('GET /api/new-wines unauth -> 401');
+ else fail(`GET /api/new-wines unauth -> ${r.status} (expected 401)`);
+ }
+
+ // (b2) unauth on /api/new-handbags
+ {
+ const r = await get({ path: '/api/new-handbags' });
+ if (r.status === 401) ok('GET /api/new-handbags unauth -> 401');
+ else fail(`GET /api/new-handbags unauth -> ${r.status} (expected 401)`);
+ }
+
+ // (b3) unauth on /api/stats
+ {
+ const r = await get({ path: '/api/stats' });
+ if (r.status === 401) ok('GET /api/stats unauth -> 401');
+ else fail(`GET /api/stats unauth -> ${r.status} (expected 401)`);
+ }
+
+ // (c) authed on /
+ {
+ const auth = 'Basic ' + Buffer.from(CREDS).toString('base64');
+ const r = await get({ path: '/', headers: { Authorization: auth } });
+ // 200 if index.html exists; or 404 if not present — but NEVER 401
+ if (r.status !== 401) ok(`GET / with creds -> ${r.status} (not 401)`);
+ else fail(`GET / with valid creds -> 401 (gate rejecting good creds)`);
+ }
+
+ // (d) bad creds -> 401
+ {
+ const auth = 'Basic ' + Buffer.from('admin:wrong').toString('base64');
+ const r = await get({ path: '/', headers: { Authorization: auth } });
+ if (r.status === 401) ok('GET / wrong creds -> 401');
+ else fail(`GET / wrong creds -> ${r.status} (expected 401)`);
+ }
+
+ } finally {
+ child.kill('SIGTERM');
+ }
+
+ if (failures.length) {
+ console.error(`\n${failures.length} failure(s):`);
+ failures.forEach((f) => console.error(' - ' + f));
+ process.exit(1);
+ }
+ console.log('\nall auth smoke tests passed.');
+ process.exit(0);
+})();
← b447df1 secure: basic-auth gate on all routes (server was public on
·
back to New Items Dashboard
·
add: pre-stage Meta Pixel snippet (placeholder; flip via _dw 03f8a8c →