chore(workspace): tighten quality gates (#2230)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-31 04:43:20 +02:00
1 parent e6e4c6f7b5
commit 5c2dca1c51
123 files changed
+4925 -5640

No files matched your search

@@ -40,8 +40,7 @@ export class DMPermissionValidator {
if (isBugHunterBotUser(senderUser)) {
return;
}
const isShadowbannedSpammer =
!senderUser.isBot && (senderUser.flags & UserFlags.SPAMMER) === UserFlags.SPAMMER;
const isShadowbannedSpammer = !senderUser.isBot && (senderUser.flags & UserFlags.SPAMMER) === UserFlags.SPAMMER;
const [senderBlockedTarget, targetBlockedSender, areFriends, targetSettings, senderSettings] = await Promise.all([
this.deps.userRepository.getRelationship(senderId, recipientId, RelationshipTypes.BLOCKED),
this.deps.userRepository.getRelationship(recipientId, senderId, RelationshipTypes.BLOCKED),
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
import {beforeAll, beforeEach, describe, expect, it} from 'vitest';
import {Logger} from '@fluxer/logger/src/Logger';
import {beforeAll, beforeEach, describe, expect, it, vi} from 'vitest';
import {createTestAccount} from '../../auth/tests/AuthTestUtils';
import type {MediaProxyMetadataRequest} from '../../infrastructure/IMediaService';
import {setInjectedMediaService} from '../../middleware/ServiceRegistry';
@@ -71,6 +72,7 @@ describe('Attachment Upload Validation', () => {
const guild = await createGuild(harness, account.token, 'Storage Unavailable Test Guild');
const channel = await createChannel(harness, account.token, guild.id, 'test-channel');
const channelId = guild.system_channel_id ?? channel.id;
const errorLoggerSpy = vi.spyOn(Logger.prototype, 'error').mockImplementation(() => {});
harness.storageService.configure({shouldFailUpload: true});
try {
const payload = {
@@ -83,8 +85,25 @@ describe('Attachment Upload Validation', () => {
const body = JSON.parse(text) as {code?: string};
expect(response.status).toBe(HTTP_STATUS.SERVICE_UNAVAILABLE);
expect(body.code).toBe(APIErrorCodes.SERVICE_UNAVAILABLE);
expect(errorLoggerSpy).toHaveBeenCalledTimes(1);
expect(errorLoggerSpy).toHaveBeenCalledWith(
{
err: expect.objectContaining({
cause: expect.objectContaining({message: 'Mock storage upload failure'}),
code: APIErrorCodes.SERVICE_UNAVAILABLE,
message: 'Attachment storage is temporarily unavailable',
status: HTTP_STATUS.SERVICE_UNAVAILABLE,
}),
method: 'POST',
path: `/channels/${channelId}/messages`,
requestId: expect.any(String),
status: HTTP_STATUS.SERVICE_UNAVAILABLE,
},
'Request failed',
);
} finally {
harness.storageService.configure({shouldFailUpload: false});
errorLoggerSpy.mockRestore();
}
});
});
@@ -160,12 +179,9 @@ describe('Attachment Upload Validation', () => {
content: 'Filename mismatch test',
attachments: [{id: 0, filename: 'expected.txt'}],
};
const {response, text, json} = await sendMessageWithAttachments(harness, account.token, channelId, payload, [
const {response, json} = await sendMessageWithAttachments(harness, account.token, channelId, payload, [
{index: 0, filename: 'different.txt', data: fileData},
]);
if (response.status !== 200) {
console.log('Error response:', text);
}
expect(response.status).toBe(200);
expect(json.attachments).toBeDefined();
expect(json.attachments).not.toBeNull();
@@ -798,8 +798,7 @@ suite('PostgresKvQueryExecutor differential', () => {
const corpus = new Map<string, Array<Row>>();
const shapeCache = new Map<string, Array<Shape>>();
const unstableShapes: Array<string> = [];
const reproducibleOrders: Array<string> = [];
const traffic = {legacy: 0, next: 0, shapes: 0, improved: 0};
const traffic = {legacy: 0, next: 0, shapes: 0};
const keysetOrderDeltas: Array<string> = [];
const legacyThrowDeltas: Array<string> = [];
@@ -906,7 +905,6 @@ suite('PostgresKvQueryExecutor differential', () => {
executor: LegacyPostgresKvQueryExecutor,
tableName: string,
shape: Shape,
candidate: string,
): Promise<boolean> {
const observed = new Set<string>();
for (const ordering of HEAP_ORDERINGS) {
@@ -914,7 +912,6 @@ suite('PostgresKvQueryExecutor differential', () => {
}
await restoreOne(kv, tableName, HEAP_ORDERINGS[0]!);
if (observed.size === 1) return false;
if (observed.has(candidate)) reproducibleOrders.push(`${tableName} ${shape.name}`);
return true;
}
@@ -950,7 +947,6 @@ suite('PostgresKvQueryExecutor differential', () => {
traffic.legacy += left.rowsRead;
traffic.next += right.rowsRead;
traffic.shapes += 1;
if (right.rowsRead < left.rowsRead) traffic.improved += 1;
if (left.fingerprint === right.fingerprint) {
if (right.rowsRead > left.rowsRead) {
mismatches.push({
@@ -976,7 +972,7 @@ suite('PostgresKvQueryExecutor differential', () => {
});
return;
}
if (!(await legacyIsOrderUnstable(kv, executor, tableName, shape, right.fingerprint))) {
if (!(await legacyIsOrderUnstable(kv, executor, tableName, shape))) {
mismatches.push({
kind: sameMultiset ? 'order' : 'rowset',
table: tableName,
@@ -1159,7 +1155,6 @@ suite('PostgresKvQueryExecutor differential', () => {
}
}
expect(shapeCount).toBeGreaterThan(3000);
console.log(`select sweep: tables=${specs.length} shapes=${shapeCount}`);
expect(mismatches.length, report(mismatches)).toBe(0);
}, 1_800_000);
@@ -1210,7 +1205,6 @@ suite('PostgresKvQueryExecutor differential', () => {
traffic.legacy += left.rowsRead;
traffic.next += right.rowsRead;
traffic.shapes += 1;
if (right.rowsRead < left.rowsRead) traffic.improved += 1;
if (right.rowsRead > left.rowsRead) {
mismatches.push({
kind: 'rowsread',
@@ -1309,7 +1303,7 @@ suite('PostgresKvQueryExecutor differential', () => {
keysetOrderDeltas.push(`${spec.name} ${label}`);
continue;
}
if (await legacyIsOrderUnstable(NEXT_TABLE, readLegacy, spec.name, shape, fingerprint(rightAll))) {
if (await legacyIsOrderUnstable(NEXT_TABLE, readLegacy, spec.name, shape)) {
unstableShapes.push(`${spec.name} ${label}`);
continue;
}
@@ -1454,14 +1448,5 @@ suite('PostgresKvQueryExecutor differential', () => {
}
expect(traffic.shapes).toBeGreaterThan(3000);
expect(traffic.next).toBeLessThan(traffic.legacy);
console.log(
`row payload traffic: shapes=${traffic.shapes} legacyRows=${traffic.legacy} nextRows=${traffic.next} reduction=${(100 - (traffic.next / traffic.legacy) * 100).toFixed(1)}% shapesImproved=${traffic.improved}`,
);
console.log(
`accepted deltas: order-unstable=${unstableShapes.length} (legacy-reproducible=${reproducibleOrders.length}) keyset-order=${keysetOrderDeltas.length} legacy-throw=${legacyThrowDeltas.length}`,
);
console.log(`order-unstable sample: ${unstableShapes.slice(0, 5).join(' | ')}`);
console.log(`keyset-order sample: ${keysetOrderDeltas.slice(0, 5).join(' | ')}`);
console.log(`legacy-throw: ${[...new Set(legacyThrowDeltas.map((entry) => entry.split(': ')[1]))].join(' | ')}`);
});
});
@@ -210,9 +210,9 @@ suite('postgres kv upgrade safety', () => {
if (pages > 200) throw new Error('runaway paging loop');
}
} catch (error) {
return {keys, error: (error as Error).message, pages};
return {keys, error: (error as Error).message};
}
return {keys, error: null, pages};
return {keys, error: null};
}
it('pages consistently with only the old image running', async () => {
@@ -232,27 +232,18 @@ suite('postgres kv upgrade safety', () => {
it('ROLLING RESTART: an old replica consumes a token minted by the new one', async () => {
const first = await next.executePagedQuery(usersScan(), {pageSize: 7, pageState: null});
expect(first.pageState).toBeTruthy();
const token = JSON.parse(Buffer.from(first.pageState!, 'base64url').toString('utf8'));
let failure: string | null = null;
let rows = 0;
try {
const second = await legacy.executePagedQuery(usersScan(), {pageSize: 7, pageState: first.pageState});
rows = second.rows.length;
} catch (error) {
failure = (error as Error).message;
}
console.log(`NEW->OLD token=${JSON.stringify(token)} legacy=${failure ?? `${rows} rows`}`);
expect(failure).toBeNull();
const second = await legacy.executePagedQuery(usersScan(), {pageSize: 7, pageState: first.pageState});
expect(second.rows).toHaveLength(7);
});
it('ROLLING RESTART: alternating old and new replicas drain the scan', async () => {
const a = await drain(['next', 'legacy'], 7);
const b = await drain(['legacy', 'next'], 7);
console.log(`new-first err=${a.error} pages=${a.pages} total=${a.keys.length} unique=${new Set(a.keys).size}`);
console.log(`old-first err=${b.error} pages=${b.pages} total=${b.keys.length} unique=${new Set(b.keys).size}`);
expect(a.error).toBeNull();
expect(a.keys).toHaveLength(40);
expect(new Set(a.keys).size).toBe(40);
expect(b.error).toBeNull();
expect(b.keys).toHaveLength(40);
expect(new Set(b.keys).size).toBe(40);
});
@@ -262,9 +253,9 @@ suite('postgres kv upgrade safety', () => {
const dual = Buffer.from(JSON.stringify({offset: first.rows.length, after: cursor.after})).toString('base64url');
const viaOld = await legacy.executePagedQuery(usersScan(), {pageSize: 7, pageState: dual});
const viaNew = await next.executePagedQuery(usersScan(), {pageSize: 7, pageState: dual});
console.log(`dual token -> old replica: ${viaOld.rows.length} rows, new replica: ${viaNew.rows.length} rows`);
expect(viaOld.rows.length).toBe(7);
expect(viaNew.rows.length).toBe(7);
expect(viaOld.rows).toHaveLength(7);
expect(viaNew.rows).toHaveLength(7);
expect(fp(viaNew.rows)).toBe(fp(viaOld.rows));
});
it('deletes exactly the same rows as the old image', async () => {
@@ -282,8 +273,7 @@ suite('postgres kv upgrade safety', () => {
const left = await raw.query<{row_key: string}>(
`SELECT row_key FROM ${KV} WHERE table_name = 'read_states' AND row_key LIKE '%400%' ORDER BY row_key`,
);
console.log(`rows left after legacy delete + new delete: ${left.rows.length}`);
expect(left.rows.length).toBe(0);
expect(left.rows).toHaveLength(0);
});
it('writes byte-identical row_key and partition_key to the old image', async () => {
@@ -333,8 +323,6 @@ suite('postgres kv upgrade safety', () => {
const cIndexes = await raw.query<{tablename: string}>(
`SELECT tablename FROM pg_indexes WHERE indexname IN ('${OLD}_row_key_c_idx', '${NEW}_row_key_c_idx')`,
);
console.log(`key diffs=${diff.rows[0]!.n} index-definition diffs=${schemaDiff.rows[0]!.n}`);
console.log(collations.rows.map((r) => `${r.tablename}.${r.attname}=${r.collname}`).join(' '));
expect(Number(diff.rows[0]!.n)).toBe(0);
expect(Number(schemaDiff.rows[0]!.n)).toBe(0);
expect(collations.rows.filter((r) => r.tablename === OLD).map((r) => r.collname)).toEqual(['default', 'default']);
@@ -358,7 +346,6 @@ suite('postgres kv upgrade safety', () => {
const indexes = await raw.query<{indexname: string}>(
`SELECT indexname FROM pg_indexes WHERE tablename = '${OLD}' ORDER BY indexname`,
);
console.log(`after boot on a legacy table: ${indexes.rows.map((r) => r.indexname).join(', ')}`);
expect(collations.rows.map((r) => r.collname)).toEqual(['default', 'default']);
expect(indexes.rows.map((r) => r.indexname)).toContain(`${OLD}_row_key_c_idx`);
});
@@ -371,10 +358,11 @@ suite('postgres kv upgrade safety', () => {
ensurePostgresKvSchema(new TableClient(raw, BOOT)),
ensurePostgresKvSchema(new TableClient(raw, BOOT)),
]);
const failures = results.filter((r) => r.status === 'rejected');
console.log(`concurrent boots rejected: ${failures.length}`);
for (const f of failures) console.log(` ${(f as PromiseRejectedResult).reason}`);
expect(failures.length).toBe(0);
expect(results).toEqual([
{status: 'fulfilled', value: undefined},
{status: 'fulfilled', value: undefined},
{status: 'fulfilled', value: undefined},
]);
}, 120_000);
it('backfills the messages partition key once and never scans for it again', async () => {
@@ -416,7 +404,6 @@ suite('postgres kv upgrade safety', () => {
await insertLegacy('m2');
await ensurePostgresKvSchema(backfillClient);
const skipped = await partitionOf('m2');
console.log(`second boot left the new legacy row alone: partition_key=${skipped === legacyKey('m2')}`);
expect(skipped).toBe(legacyKey('m2'));
expect(await partitionOf('m1')).toBe(`"c"${SEP}"b"`);
await raw.query(`DROP TABLE IF EXISTS ${BACKFILL}`);
@@ -432,7 +419,6 @@ suite('postgres kv upgrade safety', () => {
);
const after = await next.executeQuery(q);
await raw.query(`UPDATE pg_index SET indisvalid = true WHERE indexrelid = '${KV}_row_key_c_idx'::regclass`);
console.log(`index valid after boot: ${check.rows[0]?.indisvalid}`);
expect(String(after.map((r) => Object.values(r).map(String).join(',')))).toBe(
String(before.map((r) => Object.values(r).map(String).join(','))),
);
@@ -441,7 +427,6 @@ suite('postgres kv upgrade safety', () => {
describe('multi-range plan shape on a populated upgraded database', () => {
const PERF = 'kv_ranges_perf';
const SEP = String.fromCharCode(31);
const BASE = 1456074443984486400n;
let perf: IPostgresClient;
@@ -481,6 +466,7 @@ FROM generate_series($1::bigint, $1::bigint + 19999) u, generate_series(1, 3) s`
`SELECT kv.row_key, kv.row_data FROM ${PERF} kv WHERE kv.table_name = $1 AND (kv.expires_at IS NULL OR kv.expires_at > now())`,
['push_subscriptions'],
);
expect(Number.isFinite(scan.ms)).toBe(true);
const deltas: Array<string> = [];
for (const size of [5, 10, 25, 50, 100]) {
const params = {user_ids: Array.from({length: size}, (_, i) => BASE + BigInt(i * 3))};
@@ -494,9 +480,10 @@ FROM generate_series($1::bigint, $1::bigint + 19999) u, generate_series(1, 3) s`
`SELECT kv.row_key, kv.row_data FROM ${PERF} kv WHERE kv.table_name = $1${fragments.predicate} AND (kv.expires_at IS NULL OR kv.expires_at > now())`,
['push_subscriptions', ...fragments.params],
);
const verdict = `n=${size} pushdown=${pushed.ms.toFixed(2)}ms scan=${scan.ms.toFixed(2)}ms`;
console.log(verdict);
if (pushed.ms > scan.ms) deltas.push(`${verdict}\n${pushed.plan}`);
expect(Number.isFinite(pushed.ms)).toBe(true);
if (pushed.ms > scan.ms) {
deltas.push(`n=${size} pushdown=${pushed.ms.toFixed(2)}ms scan=${scan.ms.toFixed(2)}ms\n${pushed.plan}`);
}
}
expect(deltas.join('\n')).toBe('');
const overCap = buildCandidatePlan(meta, {
@@ -506,17 +493,13 @@ FROM generate_series($1::bigint, $1::bigint + 19999) u, generate_series(1, 3) s`
expect(overCap.exact).toBe(true);
const overCapGroups = planFragmentGroups(overCap.candidates);
expect(overCapGroups.map((group) => group.params.length)).toEqual([512, 512, 376]);
let chunkedMs = 0;
for (const fragments of overCapGroups) {
const pushed = await explain(
`SELECT kv.row_key, kv.row_data FROM ${PERF} kv WHERE kv.table_name = $1${fragments.predicate} AND (kv.expires_at IS NULL OR kv.expires_at > now())`,
['push_subscriptions', ...fragments.params],
);
expect(pushed.plan).not.toContain('Seq Scan');
chunkedMs += pushed.ms;
}
console.log(`n=700 chunked=${chunkedMs.toFixed(2)}ms scan=${scan.ms.toFixed(2)}ms`);
expect(SEP).toBe('\u001f');
}, 300_000);
});
});
@@ -547,8 +530,11 @@ describe('candidate plan shapes reached by real queries', () => {
: plan.candidates.kind === 'rowKeys'
? plan.candidates.rowKeys.length
: 0;
console.log(`${c.name} -> kind=${plan.candidates.kind} n=${size} exact=${plan.exact}`);
expect({kind: plan.candidates.kind, size, exact: plan.exact}, c.name).toEqual({
kind: 'ranges',
size: 100,
exact: true,
});
}
expect(true).toBe(true);
});
});
@@ -859,9 +859,7 @@ export class GuildOperationsService {
const channels = await this.channelRepository.listGuildChannels(guildId);
await Promise.all(channels.map((channel) => this.channelRepository.deleteAllChannelMessages(channel.id)));
await Promise.all(
channels.map((channel) =>
deleteChannelMessageSearchDocuments(channel.id, {context: {source: 'guild_delete'}}),
),
channels.map((channel) => deleteChannelMessageSearchDocuments(channel.id, {context: {source: 'guild_delete'}})),
);
await Promise.all(channels.map((channel) => this.channelService.attachments.purgeChannelAttachments(channel)));
const discoveryRow = await this.discoveryRepository.findByGuildId(guildId);
@@ -171,7 +171,6 @@ export async function stripNonJpegImageMetadataForUpload(
return {body: await image.gif().toBuffer(), contentType: 'image/gif'};
case 'webp':
return {body: await image.webp({lossless: true}).toBuffer(), contentType: 'image/webp'};
case 'avif':
case 'heif':
return {body: await image.avif().toBuffer(), contentType: 'image/avif'};
case 'tiff':
@@ -460,7 +459,6 @@ async function stripNonJpegImageFileToFile(
case 'webp':
await image.webp({lossless: true}).toFile(outputPath);
return {contentType: 'image/webp', ...dimensions};
case 'avif':
case 'heif':
await image.avif().toFile(outputPath);
return {contentType: 'image/avif', ...dimensions};
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {AppErrorHandler} from '@fluxer/errors/src/domains/core/ErrorHandlers';
import {ServiceUnavailableError} from '@fluxer/errors/src/domains/core/ServiceUnavailableError';
import {Logger} from '@fluxer/logger/src/Logger';
import {Hono} from 'hono';
import {describe, expect, test} from 'vitest';
import {describe, expect, test, vi} from 'vitest';
import type {HonoEnv} from '../../types/HonoEnv';
import {ConcurrencyLimitMiddleware} from '../ConcurrencyLimitMiddleware';
@@ -22,6 +24,7 @@ function createDeferred(): Deferred {
function buildApp(maxInflightRequests: number) {
const release = createDeferred();
const entered = createDeferred();
const boomError = new Error('boom');
const routes = new Hono<HonoEnv>({strict: true});
routes.use(ConcurrencyLimitMiddleware({maxInflightRequests}));
routes.get('/_health', (ctx) => ctx.text('OK'));
@@ -33,65 +36,132 @@ function buildApp(maxInflightRequests: number) {
return ctx.text('slow');
});
routes.get('/boom', () => {
throw new Error('boom');
throw boomError;
});
routes.onError(AppErrorHandler);
const app = new Hono<HonoEnv>({strict: true});
app.route('/v1', routes);
app.route('/', routes);
app.onError(AppErrorHandler);
return {app, release, entered};
return {app, release, entered, boomError};
}
describe('ConcurrencyLimitMiddleware', () => {
test('sheds with 503 once the in-flight ceiling is reached', async () => {
const {app, release, entered} = buildApp(1);
const errorLoggerSpy = vi.spyOn(Logger.prototype, 'error').mockImplementation(() => {});
const inflight = app.request('/slow');
await entered.promise;
try {
await entered.promise;
const shed = await app.request('/fast');
const shed = await app.request('/fast');
expect(shed.status).toBe(503);
expect(shed.headers.get('Retry-After')).toBe('1');
expect(await shed.json()).toMatchObject({code: 'SERVICE_UNAVAILABLE'});
expect(shed.status).toBe(503);
expect(shed.headers.get('Retry-After')).toBe('1');
expect(await shed.json()).toMatchObject({code: 'SERVICE_UNAVAILABLE'});
expect(errorLoggerSpy).toHaveBeenCalledTimes(1);
expect(errorLoggerSpy).toHaveBeenCalledWith(
{
err: expect.any(ServiceUnavailableError),
method: 'GET',
path: '/fast',
requestId: undefined,
status: 503,
},
'Request failed',
);
release.resolve();
expect((await inflight).status).toBe(200);
release.resolve();
expect((await inflight).status).toBe(200);
} finally {
release.resolve();
errorLoggerSpy.mockRestore();
}
});
test('admits probe paths while shedding everything else', async () => {
const {app, release, entered} = buildApp(1);
const errorLoggerSpy = vi.spyOn(Logger.prototype, 'error').mockImplementation(() => {});
const inflight = app.request('/slow');
await entered.promise;
try {
await entered.promise;
expect((await app.request('/_health')).status).toBe(200);
expect((await app.request('/_metrics')).status).toBe(200);
expect((await app.request('/v1/_health')).status).toBe(200);
expect((await app.request('/fast')).status).toBe(503);
expect((await app.request('/_health')).status).toBe(200);
expect((await app.request('/_metrics')).status).toBe(200);
expect((await app.request('/v1/_health')).status).toBe(200);
expect((await app.request('/fast')).status).toBe(503);
expect(errorLoggerSpy).toHaveBeenCalledTimes(1);
expect(errorLoggerSpy).toHaveBeenCalledWith(
{
err: expect.any(ServiceUnavailableError),
method: 'GET',
path: '/fast',
requestId: undefined,
status: 503,
},
'Request failed',
);
release.resolve();
await inflight;
release.resolve();
await inflight;
} finally {
release.resolve();
errorLoggerSpy.mockRestore();
}
});
test('releases the slot when the handler throws', async () => {
const {app} = buildApp(1);
for (let attempt = 0; attempt < 3; attempt += 1) {
expect((await app.request('/boom')).status).toBe(500);
const {app, boomError} = buildApp(1);
const errorLoggerSpy = vi.spyOn(Logger.prototype, 'error').mockImplementation(() => {});
try {
for (let attempt = 0; attempt < 3; attempt += 1) {
expect((await app.request('/boom')).status).toBe(500);
}
expect((await app.request('/fast')).status).toBe(200);
expect(errorLoggerSpy).toHaveBeenCalledTimes(3);
for (let callIndex = 1; callIndex <= 3; callIndex += 1) {
expect(errorLoggerSpy).toHaveBeenNthCalledWith(
callIndex,
{err: boomError, method: 'GET', path: '/boom', requestId: undefined, status: 500},
'Unhandled error occurred',
);
}
} finally {
errorLoggerSpy.mockRestore();
}
expect((await app.request('/fast')).status).toBe(200);
});
test('releases the slot taken by a shed request', async () => {
const {app, release, entered} = buildApp(1);
const errorLoggerSpy = vi.spyOn(Logger.prototype, 'error').mockImplementation(() => {});
const inflight = app.request('/slow');
await entered.promise;
try {
await entered.promise;
expect((await app.request('/fast')).status).toBe(503);
expect((await app.request('/fast')).status).toBe(503);
expect((await app.request('/fast')).status).toBe(503);
expect((await app.request('/fast')).status).toBe(503);
expect(errorLoggerSpy).toHaveBeenCalledTimes(2);
for (let callIndex = 1; callIndex <= 2; callIndex += 1) {
expect(errorLoggerSpy).toHaveBeenNthCalledWith(
callIndex,
{
err: expect.any(ServiceUnavailableError),
method: 'GET',
path: '/fast',
requestId: undefined,
status: 503,
},
'Request failed',
);
}
release.resolve();
await inflight;
release.resolve();
await inflight;
expect((await app.request('/fast')).status).toBe(200);
expect((await app.request('/fast')).status).toBe(200);
} finally {
release.resolve();
errorLoggerSpy.mockRestore();
}
});
});
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {Logger} from '@fluxer/logger/src/Logger';
import {SnowflakeType} from '@fluxer/schema/src/primitives/SchemaPrimitives';
import {Hono} from 'hono';
import {afterAll, beforeEach, describe, expect, test} from 'vitest';
import {afterAll, beforeEach, describe, expect, test, vi} from 'vitest';
import {z} from 'zod';
import {Config} from '../../Config';
import type {HonoEnv} from '../../types/HonoEnv';
@@ -52,10 +53,26 @@ describe('ResponseTypeMiddleware', () => {
test('rejects mismatching responses while validation is enabled', async () => {
const app = new Hono<HonoEnv>();
app.get('/snowflake', ResponseType(SnowflakeResponse), (ctx) => ctx.json({id: 'not-a-snowflake'}));
const errorLoggerSpy = vi.spyOn(Logger.prototype, 'error').mockImplementation(() => {});
const response = await app.request('/snowflake');
try {
const response = await app.request('/snowflake');
expect(response.status).toBe(500);
expect(response.status).toBe(500);
expect(errorLoggerSpy).toHaveBeenCalledTimes(1);
expect(errorLoggerSpy).toHaveBeenCalledWith(
{
body: {id: 'not-a-snowflake'},
method: 'GET',
path: '/snowflake',
status: 200,
validationErrors: [{message: 'INVALID_SNOWFLAKE_FORMAT', path: 'id'}],
},
'Response validation failed',
);
} finally {
errorLoggerSpy.mockRestore();
}
});
test('passes the response through untouched while validation is disabled', async () => {
+1 -2
View File
@@ -9,8 +9,7 @@ export function channelRequiresAgeVerification(
channelsById: ReadonlyMap<string, Channel>,
guildNsfw: boolean,
): boolean {
const parentCategory =
channel.parentId != null ? (channelsById.get(channel.parentId.toString()) ?? null) : null;
const parentCategory = channel.parentId != null ? (channelsById.get(channel.parentId.toString()) ?? null) : null;
return computeEffectiveChannelNsfw(
channelToContentWarningView(channel),
parentCategory ? channelToContentWarningView(parentCategory) : null,
@@ -49,6 +49,20 @@ describe('Stripe Webhook Refund', () => {
.body(payload)
.execute();
}
function useRefundListHandler(chargeId: string): void {
server.use(
http.get(
({request}) => request.url === `https://api.stripe.com/v1/refunds?charge=${chargeId}&limit=100`,
() =>
HttpResponse.json({
object: 'list',
data: [],
has_more: false,
url: '/v1/refunds',
}),
),
);
}
describe('charge.refunded', () => {
test('revokes premium and records first refund', async () => {
const account = await createTestAccount(harness);
@@ -73,6 +87,7 @@ describe('Stripe Webhook Refund', () => {
payment_intent_id: paymentIntentId,
completed_at: new Date(),
});
useRefundListHandler('ch_test_refund_123');
await sendWebhook({
type: 'charge.refunded',
data: {
@@ -121,6 +136,7 @@ describe('Stripe Webhook Refund', () => {
payment_intent_id: paymentIntentId,
completed_at: new Date(),
});
useRefundListHandler('ch_test_refund_456');
await sendWebhook({
type: 'charge.refunded',
data: {
@@ -150,6 +166,7 @@ describe('Stripe Webhook Refund', () => {
{stripe_customer_id: stripeCustomerId},
(await userRepository.findUnique(userId))!.toRow(),
);
useRefundListHandler('ch_test_refund_sub_789');
await sendWebhook({
type: 'charge.refunded',
data: {
@@ -182,6 +199,7 @@ describe('Stripe Webhook Refund', () => {
subscriptionCurrentPeriodEnd: null,
subscriptionCancelAt: null,
});
useRefundListHandler('ch_test_refund_donation_101');
const result = await sendWebhook({
type: 'charge.refunded',
data: {
@@ -234,6 +252,7 @@ describe('Stripe Webhook Refund', () => {
premium_until: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000).toISOString(),
})
.execute();
useRefundListHandler('ch_test_refund_gift_multi_123');
await sendWebhook({
type: 'charge.refunded',
data: {
@@ -287,6 +306,7 @@ describe('Stripe Webhook Refund', () => {
.get('/users/@me')
.execute();
expect(redeemerBefore.premium_type).toBe(UserPremiumTypes.SUBSCRIPTION);
useRefundListHandler('ch_test_refund_stripe_cus_only');
await sendWebhook({
type: 'charge.refunded',
data: {
@@ -325,6 +345,7 @@ describe('Stripe Webhook Refund', () => {
premium_type: UserPremiumTypes.LIFETIME,
})
.execute();
useRefundListHandler('ch_test_refund_gift_override_123');
await sendWebhook({
type: 'charge.refunded',
data: {
@@ -119,12 +119,8 @@ export class UserRelationshipService {
requestCache: RequestCache;
}): Promise<Relationship> {
const requesterUser = await this.userRepository.findUnique(userId);
const requesterIsStaff =
requesterUser != null && (requesterUser.flags & UserFlags.STAFF) === UserFlags.STAFF;
if (
!requesterIsStaff &&
(await getInstanceConfigRepository().getInstancePolicyConfig()).direct_messages_disabled
) {
const requesterIsStaff = requesterUser != null && (requesterUser.flags & UserFlags.STAFF) === UserFlags.STAFF;
if (!requesterIsStaff && (await getInstanceConfigRepository().getInstancePolicyConfig()).direct_messages_disabled) {
throw new DirectMessagesDisabledError();
}
if (staffForceAccept && requesterIsStaff) {