mirror of
https://github.com/hanzoai/sign.git
synced 2026-10-21 01:21:51 +00:00
ESLint was raised to 10, which reads eslint.config.* and nothing else. This repo still said it all in .eslintrc.cjs and .eslintignore, so every lint run ended at "couldn't find an eslint.config file" — and the pre-commit hook runs eslint over staged sources, so no commit touching a .ts file could be made. The config is the same config in the flat spelling: every rule, every ignore and the __tests__ override carried over, and the two legacy files are gone rather than left beside it to disagree. The transaction rule (inside $transaction, only `tx`) keeps both its callers. transaction.cjs is a whole flat config, so `lint:tx` still hands it to eslint alone — with --no-config-lookup, since --no-eslintrc and --ext no longer exist — and the shared config spreads it in. Its test now gives the linter the array AS SHIPPED instead of re-stating the parser beside a copy of the rules: a copy keeps passing after the original stops selecting files. 10 of 10, and `lint:tx` is clean over packages and apps. eslint-plugin-react finds the React version by asking the rule context for a filename through an API ESLint 10 removed, so detection threw on the first file. The version is stated, which never takes that path. Co-authored-by: Hanzo Dev <[email protected]>
138 lines
4.7 KiB
JavaScript
138 lines
4.7 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
|
|
const { Linter } = require('eslint');
|
|
|
|
const config = require('../transaction.cjs');
|
|
|
|
// The config AS SHIPPED, handed to the linter whole: the same array `lint:tx`
|
|
// gives eslint. Reading the rules out of it and re-stating the parser here would
|
|
// test a copy, and a copy keeps passing after the original stops selecting files.
|
|
const linter = new Linter({ configType: 'flat' });
|
|
|
|
/** @param {string} code */
|
|
const check = (code) => linter.verify(code, config, { filename: 'probe.ts' }).map((m) => m.message);
|
|
|
|
test('a transaction that only touches tx is clean', () => {
|
|
assert.deepEqual(
|
|
check(`
|
|
const envelope = await prisma.$transaction(async (tx) => {
|
|
const created = await tx.envelope.create({ data });
|
|
await tx.recipient.updateMany({ where: { envelopeId: created.id }, data });
|
|
return created;
|
|
}, { timeout: 30_000 });
|
|
|
|
await jobs.triggerJob({ name: 'send.signing.requested.email', payload });
|
|
await triggerWebhook({ event: 'DOCUMENT_CREATED', data: envelope });
|
|
await mailer.sendMail({ to, from, subject, html, text });
|
|
`),
|
|
[],
|
|
);
|
|
});
|
|
|
|
test('the shapes that carry tx are clean', () => {
|
|
assert.deepEqual(
|
|
check(`
|
|
await prisma.$transaction(async (tx) => {
|
|
await tx.$executeRaw\`pragma foreign_keys = on\`;
|
|
await tx.documentAuditLog.createMany({ data: auditLogsToCreate });
|
|
await Promise.all([tx.field.create({ data }), tx.recipient.create({ data })]);
|
|
await Promise.all(items.map(async (item) => tx.field.create({ data: item })));
|
|
await validateFieldAuth({ tx, field });
|
|
await tx.envelope.create({ data }).then((created) => created.id);
|
|
|
|
const id = generateDatabaseId('team_group');
|
|
const value = match(field.type).with(FieldType.DATE, () => date).otherwise(() => null);
|
|
});
|
|
`),
|
|
[],
|
|
);
|
|
});
|
|
|
|
test('the batch form is not an interactive transaction', () => {
|
|
assert.deepEqual(
|
|
check('await prisma.$transaction([prisma.a.findMany(), prisma.b.findMany()]);'),
|
|
[],
|
|
);
|
|
});
|
|
|
|
test('a global prisma read inside a transaction is an error', () => {
|
|
const messages = check(`
|
|
await prisma.$transaction(async (tx) => {
|
|
return await prisma.recipient.findFirst({ where });
|
|
});
|
|
`);
|
|
|
|
assert.ok(messages.some((m) => m.startsWith('no global prisma inside $transaction')));
|
|
});
|
|
|
|
test('a nested transaction is an error', () => {
|
|
const messages = check(`
|
|
await prisma.$transaction(async (tx) => {
|
|
await prisma.$transaction(async (inner) => inner.member.create({ data }));
|
|
});
|
|
`);
|
|
|
|
assert.ok(messages.some((m) => m.startsWith('no global prisma inside $transaction')));
|
|
});
|
|
|
|
test('a job trigger inside a transaction is an error, whatever the client is called', () => {
|
|
for (const client of ['jobs', 'jobsClient']) {
|
|
const [message, ...rest] = check(`
|
|
await prisma.$transaction(async (tx) => {
|
|
await tx.team.delete({ where });
|
|
await ${client}.triggerJob({ name: 'send.team-deleted.email', payload });
|
|
});
|
|
`);
|
|
|
|
assert.equal(rest.length, 0);
|
|
assert.match(message, /^no awaiting a call you did not hand tx inside \$transaction/);
|
|
}
|
|
});
|
|
|
|
test('a webhook inside a transaction is an error', () => {
|
|
const [message, ...rest] = check(`
|
|
await prisma.$transaction(async (tx) => {
|
|
const created = await tx.envelope.create({ data });
|
|
await triggerWebhook({ event: 'DOCUMENT_CREATED', data: created });
|
|
});
|
|
`);
|
|
|
|
assert.equal(rest.length, 0);
|
|
assert.match(message, /^no awaiting a call you did not hand tx inside \$transaction/);
|
|
});
|
|
|
|
test('an email inside a transaction is an error', () => {
|
|
const [message, ...rest] = check(`
|
|
await prisma.$transaction(async (tx) => {
|
|
await mailer.sendMail({ to, from, subject, html, text });
|
|
await tx.documentAuditLog.create({ data });
|
|
});
|
|
`);
|
|
|
|
assert.equal(rest.length, 0);
|
|
assert.match(message, /^no awaiting a call you did not hand tx inside \$transaction/);
|
|
});
|
|
|
|
test('any other call out of the process inside a transaction is an error', () => {
|
|
const [message, ...rest] = check(`
|
|
await prisma.$transaction(async (tx) => {
|
|
await verifyDomainWithDKIM(domain, selector, privateKey).catch((err) => { throw err; });
|
|
return await tx.emailDomain.create({ data });
|
|
});
|
|
`);
|
|
|
|
assert.equal(rest.length, 0);
|
|
assert.match(message, /^no awaiting a call you did not hand tx inside \$transaction/);
|
|
});
|
|
|
|
test('a transaction client by any other name is an error', () => {
|
|
const messages = check(`
|
|
await prisma.$transaction(async (trx) => {
|
|
await trx.envelope.create({ data });
|
|
});
|
|
`);
|
|
|
|
assert.ok(messages.some((m) => m.startsWith('name the transaction client tx')));
|
|
});
|