Repository files, collaboration, releases and project activity.
archforge-external-status / tests/http.test.jsmain
▤ tests/http.test.js3888 bytes
19 lines · Select a line number for a permalink
1
'use strict';2
const test=require('node:test'),assert=require('node:assert/strict'),crypto=require('node:crypto');3
const {openDatabase,one,run,now}=require('../src/database'),{makeApp}=require('../src/server'),{base32Encode,totp,passwordHash,encrypt}=require('../src/security');4
const key=crypto.randomBytes(32).toString('hex');5
const cfg={key,dbPath:':memory:',publicUrl:'https://status.archforge.nl/',website:'https://archforge.nl/',websiteHost:'archforge.nl',api:'',gitHttp:'',gitHost:'archforge.nl',gitPort:22,discord:'',smtpAvailable:false,smtpFrom:'',failures:3,recoveries:2,timeout:2000,interval:60000,host:'127.0.0.1',port:0,test:true};6
let app,base;7
test.before(async()=>{const db=openDatabase(':memory:');app=makeApp({db,cfg,disableMonitor:true});await new Promise(r=>app.server.listen(0,'127.0.0.1',r));base=`http://127.0.0.1:${app.server.address().port}`;});8
test.after(async()=>{await app.close();});9
test('public overview uses unknown when there are no checks',async()=>{const r=await fetch(base+'/');const text=await r.text();assert.equal(r.status,200);assert.match(text,/Monitoring in progress/);assert.match(text,/Unknown days are not counted as healthy/);assert.match(text,/status\/style.css|style.css/);});10
test('90-day page renders without status badge and bars sharing a row',async()=>{const r=await fetch(base+'/?days=90');const html=await r.text();assert.equal(r.status,200);assert.match(html,/history-bars/);assert.match(html,/month-label/);assert.doesNotMatch(html,/style="--days/);assert.match(html,/90 days/);});11
test('public API does not depend on main server',async()=>{const j=await (await fetch(base+'/api/status')).json();assert.equal(j.source,'independent-external-monitor');assert.ok(j.services.find(s=>s.id==='website'));});12
test('form requires CSRF and stores no subscription without SMTP',async()=>{const r=await fetch(base+'/subscribe');assert.equal(r.status,200);const cookie=r.headers.get('set-cookie').split(';')[0];const csrf=cookie.split('=')[1];let bad=await fetch(base+'/subscribe',{method:'POST',headers:{'cookie':cookie,'content-type':'application/x-www-form-urlencoded','origin':'https://status.archforge.nl'},body:new URLSearchParams({email:'test@example.com',csrf:'bad'})});assert.equal(bad.status,403);let good=await fetch(base+'/subscribe',{method:'POST',headers:{'cookie':cookie,'content-type':'application/x-www-form-urlencoded','origin':'https://status.archforge.nl'},body:new URLSearchParams({email:'test@example.com',csrf})});assert.equal(good.status,503);assert.equal(one(app.db,'SELECT COUNT(*) c FROM subscriptions').c,0);});13
test('admin requires valid password and time-based one-time password',async()=>{14
const secret=base32Encode(crypto.randomBytes(20));run(app.db,'INSERT INTO admin(id,username,password_hash,totp_encrypted) VALUES(1,?,?,?)','admin',passwordHash('this-is-a-strong-password'),encrypt(key,secret));15
const r=await fetch(base+'/admin/login');const cookie=r.headers.get('set-cookie').split(';')[0],csrf=cookie.split('=')[1];16
let bad=await fetch(base+'/admin/login',{method:'POST',redirect:'manual',headers:{'cookie':cookie,'content-type':'application/x-www-form-urlencoded','origin':'https://status.archforge.nl'},body:new URLSearchParams({username:'admin',password:'wrong',totp:totp(secret),csrf})});assert.equal(bad.status,401);17
const ok=await fetch(base+'/admin/login',{method:'POST',redirect:'manual',headers:{'cookie':cookie,'content-type':'application/x-www-form-urlencoded','origin':'https://status.archforge.nl'},body:new URLSearchParams({username:'admin',password:'this-is-a-strong-password',totp:totp(secret),csrf})});assert.equal(ok.status,303);assert.equal(ok.headers.get('location'),'/admin');const session=ok.headers.get('set-cookie').split(';')[0];const res=await fetch(base+'/admin',{headers:{cookie:session}});assert.equal(res.status,200);assert.match(await res.text(),/Control center/);18
});