Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions modules/express/src/typedRoutes/api/v1/pendingApproval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const pendingApprovalRequestParams = {
};

export const pendingApprovalRequestBody = {
/** State of the approval: 'approved' to approve, omit or 'rejected' to reject (defaults to rejection) */
state: optional(t.string),
/** Wallet passphrase for decrypting user keys (required for transaction signing) */
walletPassphrase: optional(t.string),
/** One-time password for 2FA verification */
Expand Down
76 changes: 74 additions & 2 deletions modules/express/test/unit/typedRoutes/pendingApproval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ describe('PendingApproval codec tests', function () {
describe('pendingApprovalRequestBody', function () {
it('should validate body with all fields', function () {
const validBody = {
state: 'approved',
walletPassphrase: 'mySecurePassword',
otp: '123456',
tx: 'transactionHexString',
Expand All @@ -58,6 +59,7 @@ describe('PendingApproval codec tests', function () {
};

const decoded = assertDecode(t.type(pendingApprovalRequestBody), validBody);
assert.strictEqual(decoded.state, validBody.state);
assert.strictEqual(decoded.walletPassphrase, validBody.walletPassphrase);
assert.strictEqual(decoded.otp, validBody.otp);
assert.strictEqual(decoded.tx, validBody.tx);
Expand All @@ -66,10 +68,13 @@ describe('PendingApproval codec tests', function () {
assert.strictEqual(decoded.pendingApprovalId, validBody.pendingApprovalId);
});

it('should validate body with no fields (all optional)', function () {
const validBody = {};
it('should validate body with only state field', function () {
const validBody = {
state: 'approved',
};

const decoded = assertDecode(t.type(pendingApprovalRequestBody), validBody);
assert.strictEqual(decoded.state, validBody.state);
assert.strictEqual(decoded.walletPassphrase, undefined);
assert.strictEqual(decoded.otp, undefined);
assert.strictEqual(decoded.tx, undefined);
Expand All @@ -80,11 +85,13 @@ describe('PendingApproval codec tests', function () {

it('should validate body with some fields', function () {
const validBody = {
state: 'rejected',
walletPassphrase: 'mySecurePassword',
otp: '123456',
};

const decoded = assertDecode(t.type(pendingApprovalRequestBody), validBody);
assert.strictEqual(decoded.state, validBody.state);
assert.strictEqual(decoded.walletPassphrase, validBody.walletPassphrase);
assert.strictEqual(decoded.otp, validBody.otp);
assert.strictEqual(decoded.tx, undefined);
Expand All @@ -93,8 +100,60 @@ describe('PendingApproval codec tests', function () {
assert.strictEqual(decoded.pendingApprovalId, undefined);
});

it('should validate body with missing state field (defaults to rejection)', function () {
const validBody = {
walletPassphrase: 'mySecurePassword',
};

const decoded = assertDecode(t.type(pendingApprovalRequestBody), validBody);
assert.strictEqual(decoded.state, undefined);
assert.strictEqual(decoded.walletPassphrase, validBody.walletPassphrase);
});

it('should validate empty body (defaults to rejection)', function () {
const validBody = {};

const decoded = assertDecode(t.type(pendingApprovalRequestBody), validBody);
assert.strictEqual(decoded.state, undefined);
assert.strictEqual(decoded.walletPassphrase, undefined);
assert.strictEqual(decoded.otp, undefined);
assert.strictEqual(decoded.tx, undefined);
assert.strictEqual(decoded.xprv, undefined);
assert.strictEqual(decoded.previewPendingTxs, undefined);
assert.strictEqual(decoded.pendingApprovalId, undefined);
});

it('should reject body with non-string state', function () {
const invalidBody = {
state: 12345, // number instead of string
};

assert.throws(() => {
assertDecode(t.type(pendingApprovalRequestBody), invalidBody);
});
});

it('should validate body with state "approved"', function () {
const validBody = {
state: 'approved',
};

const decoded = assertDecode(t.type(pendingApprovalRequestBody), validBody);
assert.strictEqual(decoded.state, 'approved');
});

it('should validate body with state "rejected"', function () {
const validBody = {
state: 'rejected',
};

const decoded = assertDecode(t.type(pendingApprovalRequestBody), validBody);
assert.strictEqual(decoded.state, 'rejected');
});

it('should reject body with non-string walletPassphrase', function () {
const invalidBody = {
state: 'approved',
walletPassphrase: 12345, // number instead of string
};

Expand All @@ -105,6 +164,7 @@ describe('PendingApproval codec tests', function () {

it('should reject body with non-string otp', function () {
const invalidBody = {
state: 'approved',
otp: 123456, // number instead of string
};

Expand All @@ -115,6 +175,7 @@ describe('PendingApproval codec tests', function () {

it('should reject body with non-string tx', function () {
const invalidBody = {
state: 'approved',
tx: 12345, // number instead of string
};

Expand All @@ -125,6 +186,7 @@ describe('PendingApproval codec tests', function () {

it('should reject body with non-string xprv', function () {
const invalidBody = {
state: 'approved',
xprv: 12345, // number instead of string
};

Expand All @@ -135,6 +197,7 @@ describe('PendingApproval codec tests', function () {

it('should reject body with non-boolean previewPendingTxs', function () {
const invalidBody = {
state: 'approved',
previewPendingTxs: 'true', // string instead of boolean
};

Expand All @@ -145,6 +208,7 @@ describe('PendingApproval codec tests', function () {

it('should reject body with non-string pendingApprovalId', function () {
const invalidBody = {
state: 'approved',
pendingApprovalId: 12345, // number instead of string
};

Expand All @@ -157,6 +221,7 @@ describe('PendingApproval codec tests', function () {
describe('Edge cases', function () {
it('should handle empty strings for string fields', function () {
const body = {
state: '',
walletPassphrase: '',
otp: '',
tx: '',
Expand All @@ -165,6 +230,7 @@ describe('PendingApproval codec tests', function () {
};

const decoded = assertDecode(t.type(pendingApprovalRequestBody), body);
assert.strictEqual(decoded.state, '');
assert.strictEqual(decoded.walletPassphrase, '');
assert.strictEqual(decoded.otp, '');
assert.strictEqual(decoded.tx, '');
Expand All @@ -174,12 +240,14 @@ describe('PendingApproval codec tests', function () {

it('should handle additional unknown properties', function () {
const body = {
state: 'approved',
walletPassphrase: 'mySecurePassword',
unknownProperty: 'some value',
};

// io-ts with t.exact() strips out additional properties
const decoded = assertDecode(t.exact(t.type(pendingApprovalRequestBody)), body);
assert.strictEqual(decoded.state, 'approved');
assert.strictEqual(decoded.walletPassphrase, 'mySecurePassword');
// @ts-expect-error - unknownProperty doesn't exist on the type
assert.strictEqual(decoded.unknownProperty, undefined);
Expand Down Expand Up @@ -352,6 +420,7 @@ describe('PendingApproval codec tests', function () {
it('should successfully preview pending transactions', async function () {
const approvalId = '123456789abcdef';
const requestBody = {
state: 'approved',
previewPendingTxs: true,
};

Expand Down Expand Up @@ -417,6 +486,7 @@ describe('PendingApproval codec tests', function () {
it('should handle SDK method failure', async function () {
const approvalId = '123456789abcdef';
const requestBody = {
state: 'approved',
walletPassphrase: 'mySecurePassword',
};

Expand All @@ -442,6 +512,7 @@ describe('PendingApproval codec tests', function () {
it('should handle invalid type in request field', async function () {
const approvalId = '123456789abcdef';
const requestBody = {
state: 'approved',
walletPassphrase: 12345, // number instead of string
};

Expand All @@ -457,6 +528,7 @@ describe('PendingApproval codec tests', function () {
it('should handle invalid previewPendingTxs type', async function () {
const approvalId = '123456789abcdef';
const requestBody = {
state: 'approved',
previewPendingTxs: 'true', // string instead of boolean
};

Expand Down