← back to New Items Dashboard

test/auth.test.js

130 lines

// 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);
})();