diff --git a/src/client/handlers/VaultsSecretsRemove.ts b/src/client/handlers/VaultsSecretsRemove.ts index bf57ba273..e6dbe7f32 100644 --- a/src/client/handlers/VaultsSecretsRemove.ts +++ b/src/client/handlers/VaultsSecretsRemove.ts @@ -6,6 +6,7 @@ import type { SecretsRemoveHeaderMessage, SecretIdentifierMessageTagged, SuccessOrErrorMessage, + VaultNamesHeaderMessage, } from '../types'; import type VaultManager from '../../vaults/VaultManager'; import type { FileSystemWritable } from '../../vaults/types'; @@ -34,18 +35,20 @@ class VaultsSecretsRemove extends DuplexHandler< ): AsyncGenerator> { const { db, vaultManager }: { db: DB; vaultManager: VaultManager } = this.container; - const vaultAcquires: Array> = []; // Extracts the header message from the iterator const headerMessage = await (async () => { const iterator = input[Symbol.asyncIterator](); - const header = (await iterator.next()).value; - if (header.type === 'VaultNamesHeaderMessage') { - if (header == null) throw new clientErrors.ErrorClientInvalidHeader(); - return header; + const header: VaultNamesHeaderMessage | SecretIdentifierMessageTagged = ( + await iterator.next() + ).value; + if (header == null || header.type !== 'VaultNamesHeaderMessage') { + throw new clientErrors.ErrorClientInvalidHeader(); } + return header; })(); // Create an array of write acquires - await db.withTransactionF(async (tran) => { + const vaultAcquires = await db.withTransactionF(async (tran) => { + const vaultAcquires: Array> = []; for (const vaultName of headerMessage.vaultNames) { const vaultIdFromName = await vaultManager.getVaultId(vaultName, tran); const vaultId = vaultIdFromName ?? vaultsUtils.decodeVaultId(vaultName); @@ -60,6 +63,7 @@ class VaultsSecretsRemove extends DuplexHandler< ); vaultAcquires.push(acquire); } + return vaultAcquires; }); // Acquire all locks in parallel and perform all operations at once yield* withG( @@ -72,7 +76,11 @@ class VaultsSecretsRemove extends DuplexHandler< } for await (const message of input) { // Ignoring any header messages - if (message.type !== 'SecretIdentifierMessage') continue; + if (message.type === 'VaultNamesHeaderMessage') { + throw new clientErrors.ErrorClientInvalidHeader( + 'The header message cannot be sent multiple times', + ); + } const efs = vaultMap.get(message.nameOrId); if (efs == null) { throw new vaultsErrors.ErrorVaultsVaultUndefined( diff --git a/src/git/utils.ts b/src/git/utils.ts index c80b956bc..cda1cbd94 100644 --- a/src/git/utils.ts +++ b/src/git/utils.ts @@ -218,7 +218,7 @@ async function listObjects({ } return; default: - utils.never(); + utils.never('Invalid type'); } } diff --git a/src/utils/utils.ts b/src/utils/utils.ts index cd4dbeda4..d14329739 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -48,7 +48,7 @@ function getDefaultNodePath(): string | undefined { return p; } -function never(message?: string): never { +function never(message: string): never { throw new utilsErrors.ErrorUtilsUndefinedBehaviour(message); } diff --git a/src/vaults/VaultInternal.ts b/src/vaults/VaultInternal.ts index b896d3ce1..2c700178d 100644 --- a/src/vaults/VaultInternal.ts +++ b/src/vaults/VaultInternal.ts @@ -571,7 +571,7 @@ class VaultInternal { } // The returned transaction can be undefined, too. We won't handle those // cases. - if (tran == null) utils.never(); + if (tran == null) utils.never('Acquired transactions cannot be null'); await tran.lock( [...this.vaultMetadataDbPath, VaultInternal.dirtyKey].join(''), ); diff --git a/tests/client/handlers/vaults.test.ts b/tests/client/handlers/vaults.test.ts index 57b256cb3..7b05bb6cc 100644 --- a/tests/client/handlers/vaults.test.ts +++ b/tests/client/handlers/vaults.test.ts @@ -1453,6 +1453,7 @@ describe('vaultsSecretsMkdir', () => { const vaultName = 'test-vault'; const vaultId = await vaultManager.createVault(vaultName); const dirPath = 'dir/dir1/dir2'; + // Attempt to make directories const response = await rpcClient.methods.vaultsSecretsMkdir(); const writer = response.writable.getWriter(); await writer.write({ @@ -1461,7 +1462,7 @@ describe('vaultsSecretsMkdir', () => { metadata: { options: { recursive: true } }, }); await writer.close(); - + // Check if the operation concluded as expected for await (const data of response.readable) { expect(data.type).toEqual('success'); } @@ -1476,13 +1477,15 @@ describe('vaultsSecretsMkdir', () => { const vaultId = await vaultManager.createVault(vaultName); const encodeVaultId = vaultsUtils.encodeVaultId(vaultId); const dirPath = 'dir/dir1/dir2'; + // Attempt to make directories const response = await rpcClient.methods.vaultsSecretsMkdir(); const writer = response.writable.getWriter(); await writer.write({ nameOrId: encodeVaultId, dirName: dirPath }); await writer.close(); + // Check if the operation concluded as expected for await (const data of response.readable) { expect(data.type).toEqual('error'); - if (data.type !== 'error') utils.never(); + if (data.type !== 'error') utils.never("Type is asserted to be 'error'"); expect(data.code).toEqual('ENOENT'); expect(data.reason).toEqual(dirPath); } @@ -1543,17 +1546,21 @@ describe('vaultsSecretsMkdir', () => { // Attempt to make directories const response = await rpcClient.methods.vaultsSecretsMkdir(); const writer = response.writable.getWriter(); - await writer.write({ nameOrId: vaultIdEncoded1, dirName: dirPath1 }); - await writer.write({ nameOrId: vaultIdEncoded2, dirName: dirPath2 }); await writer.write({ nameOrId: vaultIdEncoded1, dirName: dirPath3 }); + await writer.write({ nameOrId: vaultIdEncoded2, dirName: dirPath2 }); + await writer.write({ nameOrId: vaultIdEncoded1, dirName: dirPath1 }); await writer.close(); // Check if the operation concluded as expected + let successCount = 0; for await (const data of response.readable) { if (data.type === 'error') { expect(data.code).toEqual('ENOENT'); expect(data.reason).toEqual(dirPath3); + } else { + successCount++; } } + expect(successCount).toEqual(2); await vaultManager.withVaults( [vaultId1, vaultId2], async (vault1, vault2) => { @@ -1585,7 +1592,7 @@ describe('vaultsSecretsMkdir', () => { // Check if the operation concluded as expected for await (const data of response.readable) { expect(data.type).toEqual('error'); - if (data.type !== 'error') utils.never(); + if (data.type !== 'error') utils.never("Type is asserted to be 'error'"); expect(data.code).toEqual('EEXIST'); expect(data.reason).toEqual(dirPath); } @@ -1728,7 +1735,9 @@ describe('vaultsSecretsCat', () => { // Read response for await (const data of response.readable) { expect(data.type).toEqual('success'); - if (data.type !== 'success') utils.never(); + if (data.type !== 'success') { + utils.never("Type is asserted to be 'success'"); + } expect(data.secretContent).toEqual(secretContent); } }); @@ -1747,7 +1756,7 @@ describe('vaultsSecretsCat', () => { // Read response for await (const data of response.readable) { expect(data.type).toEqual('error'); - if (data.type !== 'error') utils.never(); + if (data.type !== 'error') utils.never("Type is asserted to be 'error'"); expect(data.code).toEqual('ENOENT'); expect(data.reason).toEqual(secretName); } @@ -1773,7 +1782,7 @@ describe('vaultsSecretsCat', () => { // Read response for await (const data of response.readable) { expect(data.type).toEqual('error'); - if (data.type !== 'error') utils.never(); + if (data.type !== 'error') utils.never("Type is asserted to be 'error'"); expect(data.code).toEqual('EISDIR'); expect(data.reason).toEqual(secretName); } @@ -1803,7 +1812,9 @@ describe('vaultsSecretsCat', () => { let totalContent = ''; for await (const data of response.readable) { expect(data.type).toEqual('success'); - if (data.type !== 'success') utils.never(); + if (data.type !== 'success') { + utils.never("Type is asserted to be 'success'"); + } totalContent += data.secretContent; } expect(totalContent).toEqual(`${secretContent1}${secretContent2}`); @@ -1845,7 +1856,9 @@ describe('vaultsSecretsCat', () => { let totalContent = ''; for await (const data of response.readable) { expect(data.type).toEqual('success'); - if (data.type !== 'success') utils.never(); + if (data.type !== 'success') { + utils.never("Type is asserted to be 'success'"); + } totalContent += data.secretContent; } expect(totalContent).toEqual( @@ -2397,7 +2410,7 @@ describe('vaultsSecretsRemove', () => { for await (const data of response.readable) { loopRun = true; expect(data.type).toStrictEqual('error'); - if (data.type !== 'error') utils.never(); + if (data.type !== 'error') utils.never("Type is asserted to be 'error'"); expect(data.code).toStrictEqual('EINVAL'); } // Check