@bsv/wallet-toolbox
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
305 lines • 13.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const listCertificates_1 = require("../../../src/storage/methods/listCertificates");
jest.mock('../../../src/storage/StorageProvider');
describe('listCertificates', () => {
let mockStorage;
let auth;
// This is a valid, minimal set of arguments for listCertificates
let vargs;
let originator;
// Helper so we can easily mock the transaction call
const mockTransaction = async (callback) => {
// We simulate the transaction by simply calling back immediately
// with an empty object as trx token.
return callback({});
};
beforeEach(() => {
// Create a fresh mock of the storage
mockStorage = {
// We only need to mock the methods that are actually used by listCertificates
transaction: jest.fn().mockImplementation(mockTransaction),
findCertificates: jest.fn(),
findCertificateFields: jest.fn(),
countCertificates: jest.fn()
};
// Auth with a valid user ID
auth = {
identityKey: 'myIdentityKey',
userId: 123
};
// Minimal valid vargs with default limit=10, offset=0, no partial filter
vargs = {
partial: undefined,
certifiers: [],
types: [],
limit: 10,
offset: 0,
privileged: false
};
// By default we set it to undefined
originator = undefined;
});
afterEach(() => {
jest.clearAllMocks();
});
test('should return an empty result when no certificates are found', async () => {
// Setup mocks
mockStorage.findCertificates.mockResolvedValueOnce([]); // no certs returned
// Execute
const result = await (0, listCertificates_1.listCertificates)(mockStorage, auth, vargs, originator);
// Verify
expect(mockStorage.transaction).toHaveBeenCalledTimes(1);
expect(mockStorage.findCertificates).toHaveBeenCalledTimes(1);
expect(mockStorage.findCertificateFields).not.toHaveBeenCalled();
expect(mockStorage.countCertificates).not.toHaveBeenCalled(); // no need to call if 0 < limit
expect(result).toEqual({
totalCertificates: 0,
certificates: []
});
});
test('should return exactly the number of certificates if they are fewer than the limit', async () => {
// Suppose we have 2 certificates
const fakeCerts = [
{
certificateId: 1,
userId: 123,
type: 'base64Type1',
serialNumber: 'serial1',
certifier: 'abcdef01',
subject: '12345678',
verifier: undefined,
revocationOutpoint: 'abcd1234.0',
signature: 'deadbeef01',
isDeleted: false,
created_at: new Date(),
updated_at: new Date()
},
{
certificateId: 2,
userId: 123,
type: 'base64Type2',
serialNumber: 'serial2',
certifier: 'abcdef02',
subject: '23456789',
verifier: undefined,
revocationOutpoint: 'abcd5678.1',
signature: 'deadbeef02',
isDeleted: false,
created_at: new Date(),
updated_at: new Date()
}
];
// Suppose each cert has some fields
const fakeFieldsForCert1 = [
{
certificateId: 1,
userId: 123,
fieldName: 'field1',
fieldValue: 'value1',
masterKey: 'mkey1',
created_at: new Date(),
updated_at: new Date()
}
];
const fakeFieldsForCert2 = [
{
certificateId: 2,
userId: 123,
fieldName: 'fieldA',
fieldValue: 'valueA',
masterKey: 'mkeyA',
created_at: new Date(),
updated_at: new Date()
}
];
mockStorage.findCertificates.mockResolvedValueOnce(fakeCerts);
// Make sure we return correct fields for each certificate
mockStorage.findCertificateFields.mockImplementation(async (args) => {
var _a, _b;
if (((_a = args.partial) === null || _a === void 0 ? void 0 : _a.certificateId) === 1)
return fakeFieldsForCert1;
if (((_b = args.partial) === null || _b === void 0 ? void 0 : _b.certificateId) === 2)
return fakeFieldsForCert2;
return [];
});
// The returned certs are length=2, which is < limit(10). So we do not call countCertificates
// Setup a spied or mocked value just in case
mockStorage.countCertificates.mockResolvedValueOnce(10);
// Execute
const result = await (0, listCertificates_1.listCertificates)(mockStorage, auth, vargs);
// Verify
expect(mockStorage.transaction).toHaveBeenCalledTimes(1);
expect(mockStorage.findCertificates).toHaveBeenCalledTimes(1);
expect(mockStorage.findCertificateFields).toHaveBeenCalledTimes(2);
expect(mockStorage.countCertificates).not.toHaveBeenCalled(); // Because 2 < 10
expect(result.certificates.length).toBe(2);
expect(result.totalCertificates).toBe(2);
// Ensure the fields are included
expect(result.certificates[0]).toEqual({
type: 'base64Type1',
subject: '12345678',
serialNumber: 'serial1',
certifier: 'abcdef01',
revocationOutpoint: 'abcd1234.0',
signature: 'deadbeef01',
verifier: undefined,
fields: { field1: 'value1' },
keyring: { field1: 'mkey1' }
});
expect(result.certificates[1]).toEqual({
type: 'base64Type2',
subject: '23456789',
serialNumber: 'serial2',
certifier: 'abcdef02',
revocationOutpoint: 'abcd5678.1',
signature: 'deadbeef02',
verifier: undefined,
fields: { fieldA: 'valueA' },
keyring: { fieldA: 'mkeyA' }
});
});
test('should call countCertificates when the returned certificates length is equal to limit', async () => {
// We want exactly 'limit' items returned, so the function calls countCertificates
vargs.limit = 2; // set limit to 2
const fakeCerts = [
{
certificateId: 11,
userId: 123,
type: 'base64Type',
serialNumber: 'sn',
certifier: 'abcdef01',
subject: 'deadbeef01',
verifier: undefined,
revocationOutpoint: '0000.0',
signature: 'abcdabcd',
isDeleted: false,
created_at: new Date(),
updated_at: new Date()
},
{
certificateId: 22,
userId: 123,
type: 'base64Type',
serialNumber: 'sn2',
certifier: 'abcdef02',
subject: 'deadbeef02',
verifier: undefined,
revocationOutpoint: '0001.0',
signature: 'ef01ef01',
isDeleted: false,
created_at: new Date(),
updated_at: new Date()
}
];
// Suppose each cert has no fields
mockStorage.findCertificateFields.mockResolvedValue([]);
// We return exactly 2 certs, which is == limit
mockStorage.findCertificates.mockResolvedValueOnce(fakeCerts);
// So the code should call countCertificates
mockStorage.countCertificates.mockResolvedValueOnce(25);
// Execute
const result = await (0, listCertificates_1.listCertificates)(mockStorage, auth, vargs);
// Verify
expect(mockStorage.findCertificates).toHaveBeenCalledTimes(1);
expect(mockStorage.findCertificateFields).toHaveBeenCalledTimes(2);
expect(mockStorage.countCertificates).toHaveBeenCalledTimes(1);
// We expect totalCertificates = 25 from countCertificates
expect(result.totalCertificates).toBe(25);
expect(result.certificates.length).toBe(2);
});
test('should handle transaction failure by throwing an error', async () => {
// If the transaction or the underlying findCertificates call fails, we rethrow
const error = new Error('Database error');
mockStorage.transaction.mockRejectedValueOnce(error);
await expect((0, listCertificates_1.listCertificates)(mockStorage, auth, vargs)).rejects.toThrow('Database error');
// Verify mocks
expect(mockStorage.transaction).toHaveBeenCalledTimes(1);
expect(mockStorage.findCertificates).not.toHaveBeenCalled();
expect(mockStorage.findCertificateFields).not.toHaveBeenCalled();
expect(mockStorage.countCertificates).not.toHaveBeenCalled();
});
test('should handle scenario userId is undefined', async () => {
// Although typically userId is required, let's see what happens
// If userId is undefined, partial: { userId: undefined, isDeleted: false } is used
// The storage call might or might not blow up. We'll test that the code still calls the transaction
// We rely on the underlying findCertificates to throw or return an empty array.
auth.userId = undefined;
mockStorage.findCertificates.mockResolvedValueOnce([]);
const result = await (0, listCertificates_1.listCertificates)(mockStorage, auth, vargs);
expect(result).toEqual({
totalCertificates: 0,
certificates: []
});
// We see a normal call
expect(mockStorage.transaction).toHaveBeenCalledTimes(1);
expect(mockStorage.findCertificates).toHaveBeenCalledTimes(1);
// The partial would have userId: undefined
const arg0 = mockStorage.findCertificates.mock.calls[0][0];
expect(arg0.partial).toEqual({
userId: undefined,
isDeleted: false
});
});
test('if returned certificate count is bigger than limit, still only returns limit items but sets total using countCertificates', async () => {
// For completeness, if the storage findCertificates method returns exactly "limit" items,
// we do a count. But let's pretend it can return exactly limit or a bit more (some storages might do that incorrectly).
// We'll only test the scenario that triggers the "else" path. Already tested an =limit scenario above,
// but let's confirm the coverage if the function doesn't rely on partial storage returning partial results.
vargs.limit = 2;
const cA = {
certificateId: 100,
userId: 123,
type: 'zzz',
serialNumber: 'snA',
certifier: 'cA',
subject: 'sA',
verifier: undefined,
revocationOutpoint: 'nope.0',
signature: 'sigA',
isDeleted: false,
created_at: new Date(),
updated_at: new Date()
};
const cB = {
certificateId: 101,
userId: 123,
type: 'yyy',
serialNumber: 'snB',
certifier: 'cB',
subject: 'sB',
verifier: undefined,
revocationOutpoint: 'nope.1',
signature: 'sigB',
isDeleted: false,
created_at: new Date(),
updated_at: new Date()
};
const cC = {
certificateId: 102,
userId: 123,
type: 'xxx',
serialNumber: 'snC',
certifier: 'cC',
subject: 'sC',
verifier: undefined,
revocationOutpoint: 'nope.2',
signature: 'sigC',
isDeleted: false,
created_at: new Date(),
updated_at: new Date()
};
// Suppose the storage returned 3 items even though we only want 2.
// The code uses them all in memory, but sees length=3 which is > limit=2
// The line checks if (r.certificates.length < paged.limit). That is false, so it calls countCertificates.
mockStorage.findCertificates.mockResolvedValueOnce([cA, cB, cC]);
// Fields are none
mockStorage.findCertificateFields.mockResolvedValue([]);
// We want to see it call countCertificates
mockStorage.countCertificates.mockResolvedValueOnce(999);
const result = await (0, listCertificates_1.listCertificates)(mockStorage, auth, vargs);
expect(result.certificates.length).toBe(3);
expect(result.totalCertificates).toBe(999); // from countCertificates
});
});
//# sourceMappingURL=listCertificates.test.js.map