fix(usage): stop local backups implying S3 use, and make the protocol-error branch reachable
Two findings from the review of the current head. Local backups no longer imply S3. markUsed derived an s3_storage marker from "a backup ran while backup_destination_type is s3" — but the middleware also counts /database-backup/* and /backup/picpeak/export as backups, and those write a local file wherever scheduled backups go. So configuring S3 and downloading a local export reported s3_storage as USED. The middleware now tells markUsed whether the operation writes to the configured destination, and only then is the marker derived. A wrong `true` in this dataset is worse than a missing signal: it is a claim about an install that nobody can check. The ProtocolError branch was dead code. adminUsage matched on `error.name === 'ProtocolError'`, but the class extends Error without setting `name`, so every instance reports 'Error' — verified — and a malformed vote or feedback payload fell through to the global handler, which logs it as an unhandled programming error and answers INTERNAL_ERROR in production, losing the validation code the caller needs. Now matched with instanceof. protocol.cjs is byte-identical with picpeak-usage (diffed against the companion repo), so the fix belongs here rather than in the class. An existing assertion needed updating for the new markUsed argument, and the path split is pinned: /backup/run is destination-driven, /database-backup/backup and /backup/picpeak/export are not. Refs #1110
This commit is contained in:
@@ -171,8 +171,33 @@ test('public/gallery paths and failed/unauthenticated admin operations never set
|
||||
simulate('/quotes', { id: 1 }, 403);
|
||||
expect(service.markUsed).not.toHaveBeenCalled();
|
||||
simulate('/customers/42/hour-entries', { id: 1 }, 200);
|
||||
// The second argument tells markUsed whether this operation writes to the
|
||||
// configured backup destination; a CRM route never does.
|
||||
expect(service.markUsed).toHaveBeenCalledWith(
|
||||
expect.arrayContaining(['crm', 'crm_hours'])
|
||||
expect.arrayContaining(['crm', 'crm_hours']),
|
||||
expect.objectContaining({ destinationBackup: false })
|
||||
);
|
||||
expect(JSON.stringify(service.markUsed.mock.calls)).not.toContain('42');
|
||||
});
|
||||
|
||||
test('only a backup that writes to the configured destination flags S3', () => {
|
||||
// /database-backup/* and /backup/picpeak/export produce a local file, so
|
||||
// they must not imply S3 use just because S3 is the configured destination.
|
||||
const seen = [];
|
||||
const simulate = (pathname) => {
|
||||
service.markUsed.mockClear();
|
||||
const res = new (require('events').EventEmitter)();
|
||||
res.statusCode = 200;
|
||||
productUsage({ path: pathname, admin: { id: 1 } }, res, () => {});
|
||||
res.emit('finish');
|
||||
seen.push([pathname, service.markUsed.mock.calls[0]?.[1]?.destinationBackup]);
|
||||
};
|
||||
simulate('/backup/run');
|
||||
simulate('/database-backup/backup');
|
||||
simulate('/backup/picpeak/export');
|
||||
expect(seen).toEqual([
|
||||
['/backup/run', true],
|
||||
['/database-backup/backup', false],
|
||||
['/backup/picpeak/export', false],
|
||||
]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user