@bsv/wallet-toolbox
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
735 lines • 36.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const WalletPermissionsManager_fixtures_1 = require("./WalletPermissionsManager.fixtures");
const WalletPermissionsManager_1 = require("../WalletPermissionsManager");
jest.mock('@bsv/sdk', () => WalletPermissionsManager_fixtures_1.MockedBSV_SDK);
/**
* Test suite for Permission Modules
* Tests both P-basket and P-protocol delegation to custom permission modules.
*/
describe('WalletPermissionsManager - Permission Module Support', () => {
let underlying;
beforeEach(() => {
underlying = (0, WalletPermissionsManager_fixtures_1.mockUnderlyingWallet)();
});
describe('P-Label Delegation', () => {
it('should delegate listActions through P-label modules in order and return responses in reverse order', async () => {
const callOrder = [];
const module1 = {
onRequest: jest.fn(async (req) => {
callOrder.push('req1');
expect(req.args.req1Processed).toBeUndefined();
return { ...req, args: { ...req.args, req1Processed: true } };
}),
onResponse: jest.fn(async (res) => {
callOrder.push('res1');
expect(res.processedBy).toBe('module2');
return { ...res, finalProcessedBy: 'module1' };
})
};
const module2 = {
onRequest: jest.fn(async (req) => {
callOrder.push('req2');
expect(req.args.req1Processed).toBe(true);
return { ...req, args: { ...req.args, req2Processed: true } };
}),
onResponse: jest.fn(async (res) => {
callOrder.push('res2');
expect(res.processedBy).toBeUndefined();
return { ...res, processedBy: 'module2' };
})
};
const config = {
permissionModules: {
scheme1: module1,
scheme2: module2
},
seekPermissionWhenListingActionsByLabel: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.listActions.mockResolvedValue({ totalActions: 0, actions: [] });
const result = await manager.listActions({
labels: ['p scheme1 alpha', 'p scheme2 beta', 'regular-label']
}, 'app.com');
expect(module1.onRequest).toHaveBeenCalledTimes(1);
expect(module2.onRequest).toHaveBeenCalledTimes(1);
expect(module1.onResponse).toHaveBeenCalledTimes(1);
expect(module2.onResponse).toHaveBeenCalledTimes(1);
expect(callOrder).toEqual(['req1', 'req2', 'res2', 'res1']);
expect(result.finalProcessedBy).toBe('module1');
});
it('should delegate createAction when a P-label is present', async () => {
const testModule = {
onRequest: jest.fn(async (req) => req),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
labels: testModule
},
seekSpendingPermissions: false,
seekBasketInsertionPermissions: false,
seekPermissionWhenApplyingActionLabels: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.createAction.mockResolvedValue({ txid: 'abc123', tx: [] });
await manager.createAction({
description: 'Label-based createAction',
labels: ['p labels token', 'regular-label'],
outputs: [
{
lockingScript: 'abcd',
satoshis: 1000,
basket: 'regular-basket',
outputDescription: 'Test output'
}
]
}, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
expect(testModule.onResponse).toHaveBeenCalledTimes(1);
});
it('should delegate internalizeAction when a P-label is present', async () => {
const testModule = {
onRequest: jest.fn(async (req) => req),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
labels: testModule
},
seekPermissionWhenApplyingActionLabels: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.internalizeAction.mockResolvedValue({ accepted: true });
await manager.internalizeAction({
tx: [],
description: 'Internalize with label',
labels: ['p labels internal', 'regular-label'],
outputs: []
}, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
expect(testModule.onResponse).toHaveBeenCalledTimes(1);
});
});
afterEach(() => {
jest.clearAllMocks();
});
describe('Permission Module Registration', () => {
it('should accept permissionModules in config', () => {
var _a;
const testModule = {
onRequest: jest.fn(async (req) => req),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
'test-scheme': testModule
}
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
// Verify module is stored in config
const storedConfig = manager.config;
expect(storedConfig.permissionModules).toBeDefined();
expect((_a = storedConfig.permissionModules) === null || _a === void 0 ? void 0 : _a['test-scheme']).toBe(testModule);
});
it('should support multiple permission modules for different schemes', () => {
var _a, _b;
const module1 = {
onRequest: jest.fn(async (req) => req),
onResponse: jest.fn(async (res) => res)
};
const module2 = {
onRequest: jest.fn(async (req) => req),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
scheme1: module1,
scheme2: module2
}
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
const storedConfig = manager.config;
expect(Object.keys(storedConfig.permissionModules || {}).length).toBe(2);
expect((_a = storedConfig.permissionModules) === null || _a === void 0 ? void 0 : _a['scheme1']).toBe(module1);
expect((_b = storedConfig.permissionModules) === null || _b === void 0 ? void 0 : _b['scheme2']).toBe(module2);
});
});
describe('P-Basket Delegation - listOutputs', () => {
it('should delegate to permission when basket starts with "p "', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
// Module can inspect and transform the request
expect(req.method).toBe('listOutputs');
expect(req.args.basket).toBe('p myscheme some-data');
// Transform basket for underlying call
return {
...req,
args: { ...req.args, basket: 'transformed-basket' }
};
}),
onResponse: jest.fn(async (res) => {
// Module can transform the response
return { ...res, transformedByModule: true };
})
};
const config = {
permissionModules: {
myscheme: testModule
}
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
// Mock underlying response
underlying.listOutputs.mockResolvedValue({ outputs: [] });
const result = await manager.listOutputs({ basket: 'p myscheme some-data' }, 'app.com');
// Verify module's onRequest was called
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
// Note: args array includes both the request args and originator
expect(testModule.onRequest).toHaveBeenCalledWith({
method: 'listOutputs',
args: { basket: 'p myscheme some-data' },
originator: 'app.com'
});
// Verify underlying was called with transformed basket
// Note: originator stays as 'app.com' not changed to admin
expect(underlying.listOutputs).toHaveBeenCalledWith({ basket: 'transformed-basket' }, 'app.com');
// Verify module's onResponse was called
expect(testModule.onResponse).toHaveBeenCalledTimes(1);
// Verify response was transformed
expect(result).toHaveProperty('transformedByModule', true);
});
it('should throw error if P-basket scheme has no registered module', async () => {
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com');
await expect(manager.listOutputs({ basket: 'p unregistered-scheme data' }, 'app.com')).rejects.toThrow(/Unsupported P-module scheme: p unregistered-scheme/);
});
it('should handle normal baskets without delegation', async () => {
const testModule = {
onRequest: jest.fn(async (req) => req),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
myscheme: testModule
},
seekBasketListingPermissions: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.listOutputs.mockResolvedValue({ outputs: [] });
await manager.listOutputs({ basket: 'normal-basket' }, 'app.com');
// Module should NOT be called for normal baskets
expect(testModule.onRequest).not.toHaveBeenCalled();
expect(testModule.onResponse).not.toHaveBeenCalled();
// Underlying should be called with original basket
expect(underlying.listOutputs).toHaveBeenCalledWith({ basket: 'normal-basket' }, 'app.com');
});
it('should decrypt metadata in P-basket responses', async () => {
const testModule = {
onRequest: jest.fn(async (req) => req),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
myscheme: testModule
},
encryptWalletMetadata: true
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
// Mock decryption
const decryptMetadata = jest.spyOn(manager, 'decryptListOutputsMetadata');
decryptMetadata.mockImplementation(async (outputs) => outputs);
underlying.listOutputs.mockResolvedValue({
outputs: [{ outpoint: 'txid.0', satoshis: 100 }]
});
await manager.listOutputs({ basket: 'p myscheme data' }, 'app.com');
// Verify metadata decryption was called
expect(decryptMetadata).toHaveBeenCalled();
});
});
describe('P-Basket Delegation - relinquishOutput', () => {
it('should delegate to P-module when basket starts with "p "', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
expect(req.method).toBe('relinquishOutput');
expect(req.args.basket).toBe('p token admin-basket-data');
return {
...req,
args: { ...req.args, basket: 'admin-real-basket' }
};
}),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
token: testModule
}
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.relinquishOutput.mockResolvedValue(undefined);
await manager.relinquishOutput({ basket: 'p token admin-basket-data', output: 'txid.0' }, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
// Note: originator remains 'app.com' - only basket is transformed
expect(underlying.relinquishOutput).toHaveBeenCalledWith({ basket: 'admin-real-basket', output: 'txid.0' }, 'app.com');
});
});
describe('P-Basket Delegation - createAction', () => {
it('should delegate to P-module for outputs with P-baskets', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
// Verify we receive createAction request
expect(req.method).toBe('createAction');
return req;
}),
onResponse: jest.fn(async (res) => {
// Module can inspect the transaction result
return res;
})
};
const config = {
permissionModules: {
bottle: testModule
},
seekSpendingPermissions: false,
seekBasketInsertionPermissions: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.createAction.mockResolvedValue({
txid: 'abc123',
tx: []
});
await manager.createAction({
description: 'Test action',
outputs: [
{
lockingScript: 'abcd',
satoshis: 1000,
basket: 'p bottle token-data',
outputDescription: 'Test output'
}
]
}, 'app.com');
// Verify P-module was invoked
expect(testModule.onRequest).toHaveBeenCalled();
expect(testModule.onResponse).toHaveBeenCalled();
});
it('should chain multiple P-modules in correct order: req1->req2->req3 then res3->res2->res1', async () => {
const callOrder = [];
const module1 = {
onRequest: jest.fn(async (req) => {
callOrder.push('req1');
// First module receives original args without any processing markers
expect(req.args.req1Processed).toBeUndefined();
expect(req.args.req2Processed).toBeUndefined();
// Transform args - add marker to track this module processed them
return {
...req,
args: { ...req.args, req1Processed: true }
};
}),
onResponse: jest.fn(async (res) => {
callOrder.push('res1');
// Last module in response chain should see transformations from res2 and res3
expect(res.processedBy).toBe('module2');
return { ...res, finalProcessedBy: 'module1' };
})
};
const module2 = {
onRequest: jest.fn(async (req) => {
callOrder.push('req2');
// Second module receives args transformed by module1
// (each module gets fresh request object, but args are chained)
expect(req.args.req1Processed).toBe(true);
expect(req.args.req2Processed).toBeUndefined();
return {
...req,
args: { ...req.args, req2Processed: true }
};
}),
onResponse: jest.fn(async (res) => {
callOrder.push('res2');
// Second-to-last in response chain should see transformation from res3
expect(res.processedBy).toBe('module3');
return { ...res, processedBy: 'module2' };
})
};
const module3 = {
onRequest: jest.fn(async (req) => {
callOrder.push('req3');
// Third module receives args with transformations from both module1 and module2
expect(req.args.req1Processed).toBe(true);
expect(req.args.req2Processed).toBe(true);
expect(req.args.req3Processed).toBeUndefined();
return {
...req,
args: { ...req.args, req3Processed: true }
};
}),
onResponse: jest.fn(async (res) => {
callOrder.push('res3');
// First module in response chain receives raw response from underlying wallet
expect(res.processedBy).toBeUndefined();
return { ...res, processedBy: 'module3' };
})
};
const config = {
permissionModules: {
scheme1: module1,
scheme2: module2,
scheme3: module3
},
seekSpendingPermissions: false,
seekBasketInsertionPermissions: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.createAction.mockResolvedValue({ txid: 'abc', tx: [] });
const result = await manager.createAction({
description: 'Multi-module chain test',
outputs: [
{ lockingScript: '1234', satoshis: 100, basket: 'p scheme1 data1', outputDescription: 'Output 1' },
{ lockingScript: '5678', satoshis: 200, basket: 'p scheme2 data2', outputDescription: 'Output 2' },
{ lockingScript: '9abc', satoshis: 300, basket: 'p scheme3 data3', outputDescription: 'Output 3' }
]
}, 'app.com');
// Verify all modules were called
expect(module1.onRequest).toHaveBeenCalledTimes(1);
expect(module2.onRequest).toHaveBeenCalledTimes(1);
expect(module3.onRequest).toHaveBeenCalledTimes(1);
expect(module1.onResponse).toHaveBeenCalledTimes(1);
expect(module2.onResponse).toHaveBeenCalledTimes(1);
expect(module3.onResponse).toHaveBeenCalledTimes(1);
// Verify correct order: req1 -> req2 -> req3 then res3 -> res2 -> res1
expect(callOrder).toEqual(['req1', 'req2', 'req3', 'res3', 'res2', 'res1']);
// Verify final result has the complete chain of transformations
expect(result.finalProcessedBy).toBe('module1');
});
});
describe('P-Basket Delegation - internalizeAction', () => {
it('should delegate to P-module when P-basket is specified in insertionRemittance', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
expect(req.method).toBe('internalizeAction');
return req;
}),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
myscheme: testModule
},
encryptWalletMetadata: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.internalizeAction.mockResolvedValue(undefined);
await manager.internalizeAction({
tx: [],
description: 'Test internalize action',
outputs: [
{
outputIndex: 0,
protocol: 'basket insertion',
paymentRemittance: { derivationPrefix: '', derivationSuffix: '', senderIdentityKey: '' },
insertionRemittance: {
basket: 'p myscheme data',
customInstructions: ''
}
}
] // Use 'as any' to avoid strict type checking in test
}, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
expect(testModule.onResponse).toHaveBeenCalledTimes(1);
});
});
describe('P-Protocol Delegation', () => {
it('should delegate getPublicKey to P-protocol module', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
expect(req.method).toBe('getPublicKey');
expect(req.args.protocolID).toEqual([0, 'p bottle test']);
return req;
}),
onResponse: jest.fn(async (res) => {
// Module can verify the key or add metadata
return { ...res, verifiedByModule: true };
})
};
const config = {
permissionModules: {
bottle: testModule
},
seekPermissionsForPublicKeyRevelation: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.getPublicKey.mockResolvedValue({ publicKey: '02abc...' });
const result = await manager.getPublicKey({
protocolID: [0, 'p bottle test'],
keyID: '1',
counterparty: 'self'
}, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
expect(testModule.onResponse).toHaveBeenCalledTimes(1);
expect(result).toHaveProperty('verifiedByModule', true);
});
it('should delegate createSignature to P-protocol module', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
expect(req.method).toBe('createSignature');
expect(req.args.protocolID).toEqual([1, 'p token spend']);
// Module can validate spend amounts, check limits, etc.
return req;
}),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
token: testModule
},
seekProtocolPermissionsForSigning: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.createSignature.mockResolvedValue({ signature: 'abc123' });
await manager.createSignature({
protocolID: [1, 'p token spend'],
keyID: '1',
data: [0x01, 0x02]
}, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
expect(testModule.onResponse).toHaveBeenCalledTimes(1);
});
it('should delegate verifySignature to P-protocol module', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
expect(req.method).toBe('verifySignature');
return req;
}),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
secure: testModule
}
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.verifySignature.mockResolvedValue(true);
await manager.verifySignature({
protocolID: [1, 'p secure verify'],
keyID: '1',
data: [0x01],
signature: [0x02]
}, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
});
it('should delegate encrypt to P-protocol module', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
expect(req.method).toBe('encrypt');
expect(req.args.protocolID).toEqual([2, 'p secure encrypt']);
return req;
}),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
secure: testModule
},
seekProtocolPermissionsForEncrypting: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.encrypt.mockResolvedValue({ ciphertext: [0x01, 0x02] });
await manager.encrypt({
protocolID: [2, 'p secure encrypt'],
keyID: '1',
plaintext: [0x48, 0x65, 0x6c, 0x6c, 0x6f]
}, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
});
it('should delegate decrypt to P-protocol module', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
expect(req.method).toBe('decrypt');
return req;
}),
onResponse: jest.fn(async (res) => {
// Module could verify decrypted content
return res;
})
};
const config = {
permissionModules: {
secure: testModule
},
seekProtocolPermissionsForEncrypting: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.decrypt.mockResolvedValue({ plaintext: [0x48, 0x65] });
await manager.decrypt({
protocolID: [2, 'p secure decrypt'],
keyID: '1',
ciphertext: [0x01, 0x02]
}, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
expect(testModule.onResponse).toHaveBeenCalledTimes(1);
});
it('should delegate createHmac to P-protocol module', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
expect(req.method).toBe('createHmac');
return req;
}),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
hmac: testModule
},
seekProtocolPermissionsForHMAC: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.createHmac.mockResolvedValue({ hmac: [0x01] });
await manager.createHmac({
protocolID: [2, 'p hmac test'],
keyID: '1',
data: [0x48]
}, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
});
it('should delegate verifyHmac to P-protocol module', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
expect(req.method).toBe('verifyHmac');
return req;
}),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
hmac: testModule
}
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.verifyHmac.mockResolvedValue(true);
await manager.verifyHmac({
protocolID: [2, 'p hmac verify'],
keyID: '1',
data: [0x48],
hmac: [0x01]
}, 'app.com');
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
});
});
describe('P-Module Error Handling', () => {
it('should reject invalid P-label formats', async () => {
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', {});
await expect(manager.listActions({ labels: ['p schemeOnly'] }, 'app.com')).rejects.toThrow('Invalid P-label format');
await expect(manager.listActions({ labels: ['p missingScheme'] }, 'app.com')).rejects.toThrow('Invalid P-label format');
await expect(manager.listActions({ labels: ['p scheme '] }, 'app.com')).rejects.toThrow('Invalid P-label format');
expect(underlying.listActions).not.toHaveBeenCalled();
});
it('should throw if P-label scheme is unsupported', async () => {
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', {});
await expect(manager.listActions({ labels: ['p unknown scheme', 'regular-label'] }, 'app.com')).rejects.toThrow('Unsupported P-label scheme: p unknown');
expect(underlying.listActions).not.toHaveBeenCalled();
});
it('should throw if P-module onRequest throws', async () => {
const testModule = {
onRequest: jest.fn(async () => {
throw new Error('Module validation failed');
}),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
failing: testModule
}
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
await expect(manager.listOutputs({ basket: 'p failing data' }, 'app.com')).rejects.toThrow('Module validation failed');
// Underlying should not be called if module fails
expect(underlying.listOutputs).not.toHaveBeenCalled();
});
it('should throw if P-module onResponse throws', async () => {
const testModule = {
onRequest: jest.fn(async (req) => req),
onResponse: jest.fn(async () => {
throw new Error('Module response processing failed');
})
};
const config = {
permissionModules: {
failing: testModule
}
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.listOutputs.mockResolvedValue({ outputs: [] });
await expect(manager.listOutputs({ basket: 'p failing data' }, 'app.com')).rejects.toThrow('Module response processing failed');
// Underlying was called, but response processing failed
expect(underlying.listOutputs).toHaveBeenCalled();
});
it('should call P-module onRequest even when permission checks are enabled', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
// Module validation happens before permission checks
return req;
}),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
myscheme: testModule
},
seekProtocolPermissionsForSigning: false // Disable to simplify test
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
underlying.createSignature.mockResolvedValue({ signature: [0x01] });
// Create a signature request with P-protocol
await manager.createSignature({
protocolID: [1, 'p myscheme sign'],
keyID: '1',
data: [0x01]
}, 'app.com');
// Module should have been called
expect(testModule.onRequest).toHaveBeenCalled();
expect(testModule.onResponse).toHaveBeenCalled();
});
});
describe('P-Module Admin Bypass', () => {
it('should still use P-module even for admin originator', async () => {
const testModule = {
onRequest: jest.fn(async (req) => {
// Module should be called even for admin
expect(req.originator).toBe('admin.com');
return req;
}),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
myscheme: testModule
}
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'admin.com', config);
underlying.listOutputs.mockResolvedValue({ outputs: [] });
await manager.listOutputs({ basket: 'p myscheme data' }, 'admin.com');
// Module should be invoked even for admin calls
expect(testModule.onRequest).toHaveBeenCalledTimes(1);
expect(testModule.onResponse).toHaveBeenCalledTimes(1);
});
it('should still block non-admin access to admin baskets even with P-module', async () => {
const testModule = {
onRequest: jest.fn(async (req) => req),
onResponse: jest.fn(async (res) => res)
};
const config = {
permissionModules: {
myscheme: testModule
},
seekBasketListingPermissions: false
};
const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'customToken.domain.com', config);
// Try to access admin basket with non-admin originator
// Admin baskets are prefixed with 'admin_' by convention
await expect(manager.listOutputs({ basket: 'admin_someAdminBasket' }, 'app.com')).rejects.toThrow(/admin-only/);
// P-module should NOT have been called since admin check happens before P-module delegation
expect(testModule.onRequest).not.toHaveBeenCalled();
});
});
});
//# sourceMappingURL=WalletPermissionsManager.pmodules.test.js.map