140 lines
4.6 KiB
JavaScript
140 lines
4.6 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const { spawnSync } = require('node:child_process');
|
|
|
|
const rootDir = process.cwd();
|
|
|
|
test('web and worker boot cleanly in direct node mode', (t) => {
|
|
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mastermind-startup-'));
|
|
const env = {
|
|
...process.env,
|
|
DATA_DIR: dataDir,
|
|
DATABASE_URL: 'memory://smoke',
|
|
SESSION_SECRET: 'smoke-test-secret',
|
|
BOOTSTRAP_OWNER_EMAIL: 'owner@local',
|
|
BOOTSTRAP_OWNER_PASSWORD: 'owner',
|
|
MASTERMIND_DISABLE_LISTEN: '1',
|
|
NODE_ENV: 'test',
|
|
WORKER_TICK_MS: '100',
|
|
WORKER_RUN_ONCE: '1'
|
|
};
|
|
|
|
t.after(() => {
|
|
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
});
|
|
|
|
const web = spawnSync(process.execPath, ['web/src/index.js'], {
|
|
cwd: rootDir,
|
|
env,
|
|
encoding: 'utf8'
|
|
});
|
|
const worker = spawnSync(process.execPath, ['worker/src/worker.js'], {
|
|
cwd: rootDir,
|
|
env,
|
|
encoding: 'utf8'
|
|
});
|
|
|
|
assert.equal(web.status, 0, web.stderr || web.stdout);
|
|
assert.equal(worker.status, 0, worker.stderr || worker.stdout);
|
|
});
|
|
|
|
test('memory-backed flow supports project creation, email sorting, and draft generation', async () => {
|
|
const [{ createMemoryPool }, { applyRulesToEmail }, { buildPCOBody }] = await Promise.all([
|
|
import('../web/src/lib/memory-db.js'),
|
|
import('../web/src/lib/email-rules.js'),
|
|
import('../web/src/lib/utils.js')
|
|
]);
|
|
|
|
const pool = createMemoryPool();
|
|
|
|
const ownerInsert = await pool.query(
|
|
"insert into users(email, display_name, role) values ($1,'Owner','owner') returning id",
|
|
['owner@local']
|
|
);
|
|
const ownerId = ownerInsert.rows[0].id;
|
|
await pool.query(
|
|
"insert into identities(user_id, provider, email, password_hash) values ($1,'local',$2,$3)",
|
|
[ownerId, 'owner@local', 'hash']
|
|
);
|
|
|
|
const projectInsert = await pool.query(
|
|
`insert into projects(created_by_user_id,name,job_number,role_mode,gc_name,city,state,keywords)
|
|
values ($1,$2,$3,$4,$5,$6,$7,$8)
|
|
returning id`,
|
|
[ownerId, 'Smoke Test Project', '0222600001', 'ec', 'Example GC', 'Durham', 'NC', 'smoke']
|
|
);
|
|
const projectId = projectInsert.rows[0].id;
|
|
await pool.query(
|
|
`insert into project_members(project_id,user_id,project_role) values ($1,$2,'owner')
|
|
on conflict do nothing`,
|
|
[projectId, ownerId]
|
|
);
|
|
|
|
await pool.query(
|
|
`insert into email_rules(created_by_user_id, project_id, match_type, match_value, priority)
|
|
values ($1,$2,$3,$4,$5)
|
|
returning id`,
|
|
[ownerId, projectId, 'subject_contains', '0222600001', 10]
|
|
);
|
|
|
|
const emailInsert = await pool.query(
|
|
`insert into ingested_emails(created_by_user_id, source, from_addr, to_addr, cc_addr, subject, date, body_text, body_html, has_attachments, raw_path, sha256, status)
|
|
values ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,'unsorted')
|
|
returning *`,
|
|
[
|
|
ownerId,
|
|
'upload',
|
|
'pm@example.com',
|
|
'team@example.com',
|
|
'',
|
|
'RE: 0222600001 coordination',
|
|
new Date('2026-03-01T12:00:00.000Z'),
|
|
'Need pricing and schedule impact.',
|
|
'',
|
|
false,
|
|
'/tmp/sample.eml',
|
|
'abc123'
|
|
]
|
|
);
|
|
const email = emailInsert.rows[0];
|
|
|
|
const rules = (await pool.query(
|
|
`select * from email_rules where enabled=true order by priority asc, created_at asc`
|
|
)).rows;
|
|
const match = await applyRulesToEmail(email, rules);
|
|
assert.deepEqual(
|
|
{ projectId: match.projectId, confidence: match.confidence },
|
|
{ projectId, confidence: 0.9 }
|
|
);
|
|
|
|
await pool.query(
|
|
`update ingested_emails set project_id=$1, status=$2, confidence=$3 where id=$4`,
|
|
[projectId, 'assigned', match.confidence, email.id]
|
|
);
|
|
|
|
const assignedEmails = await pool.query(
|
|
`select id, from_addr, subject, date, source
|
|
from ingested_emails
|
|
where project_id=$1
|
|
order by date desc nulls last, created_at desc
|
|
limit 300`,
|
|
[projectId]
|
|
);
|
|
assert.equal(assignedEmails.rows.length, 1);
|
|
assert.equal(assignedEmails.rows[0].subject, 'RE: 0222600001 coordination');
|
|
|
|
const body = buildPCOBody(email, { name: 'Smoke Test Project', job_number: '0222600001' });
|
|
const draftInsert = await pool.query(
|
|
`insert into pco_drafts(created_by_user_id, project_id, source_email_id, title, body)
|
|
values ($1,$2,$3,$4,$5) returning id`,
|
|
[ownerId, projectId, email.id, 'PCO: RE: 0222600001 coordination', body]
|
|
);
|
|
|
|
const draft = await pool.query('select * from pco_drafts where id=$1', [draftInsert.rows[0].id]);
|
|
assert.equal(draft.rows[0].source_email_id, email.id);
|
|
assert.match(draft.rows[0].body, /Need pricing and schedule impact\./);
|
|
});
|