95 lines
3.2 KiB
TypeScript
95 lines
3.2 KiB
TypeScript
|
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||
|
|
import * as smartsecret from '../ts/index.js';
|
||
|
|
import * as path from 'path';
|
||
|
|
import * as fs from 'fs';
|
||
|
|
import * as os from 'os';
|
||
|
|
|
||
|
|
let testDir: string;
|
||
|
|
let instance: smartsecret.SmartSecret;
|
||
|
|
|
||
|
|
tap.test('setup - create temp directory', async () => {
|
||
|
|
testDir = path.join(os.tmpdir(), `smartsecret-test-${Date.now()}`);
|
||
|
|
fs.mkdirSync(testDir, { recursive: true });
|
||
|
|
instance = new smartsecret.SmartSecret({
|
||
|
|
service: 'smartsecret-test',
|
||
|
|
vaultPath: path.join(testDir, 'vault.json'),
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('should report file-encrypted backend type', async () => {
|
||
|
|
const backendType = await instance.getBackendType();
|
||
|
|
// On CI/test environments without keychain, should fall back to file
|
||
|
|
expect(backendType).toEqual('file-encrypted');
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('should set and get a secret', async () => {
|
||
|
|
await instance.setSecret('test-account', 'my-secret-value');
|
||
|
|
const result = await instance.getSecret('test-account');
|
||
|
|
expect(result).toEqual('my-secret-value');
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('should return null for non-existent secret', async () => {
|
||
|
|
const result = await instance.getSecret('nonexistent');
|
||
|
|
expect(result).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('should overwrite an existing secret', async () => {
|
||
|
|
await instance.setSecret('test-account', 'updated-secret');
|
||
|
|
const result = await instance.getSecret('test-account');
|
||
|
|
expect(result).toEqual('updated-secret');
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('should handle special characters in secrets', async () => {
|
||
|
|
const specialSecret = '!@#$%^&*()_+-=[]{}|;:\'",.<>?/\\`~\n\ttabs and newlines';
|
||
|
|
await instance.setSecret('special-chars', specialSecret);
|
||
|
|
const result = await instance.getSecret('special-chars');
|
||
|
|
expect(result).toEqual(specialSecret);
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('should handle empty string secrets', async () => {
|
||
|
|
await instance.setSecret('empty-secret', '');
|
||
|
|
const result = await instance.getSecret('empty-secret');
|
||
|
|
expect(result).toEqual('');
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('should list accounts', async () => {
|
||
|
|
const accounts = await instance.listAccounts();
|
||
|
|
expect(accounts).toContain('test-account');
|
||
|
|
expect(accounts).toContain('special-chars');
|
||
|
|
expect(accounts).toContain('empty-secret');
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('should delete a secret', async () => {
|
||
|
|
const deleted = await instance.deleteSecret('test-account');
|
||
|
|
expect(deleted).toBeTrue();
|
||
|
|
const result = await instance.getSecret('test-account');
|
||
|
|
expect(result).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('should return false when deleting non-existent secret', async () => {
|
||
|
|
const deleted = await instance.deleteSecret('nonexistent');
|
||
|
|
expect(deleted).toBeFalse();
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('should isolate secrets by service', async () => {
|
||
|
|
const otherInstance = new smartsecret.SmartSecret({
|
||
|
|
service: 'other-service',
|
||
|
|
vaultPath: path.join(testDir, 'vault.json'),
|
||
|
|
});
|
||
|
|
|
||
|
|
await otherInstance.setSecret('shared-name', 'other-value');
|
||
|
|
await instance.setSecret('shared-name', 'original-value');
|
||
|
|
|
||
|
|
const otherResult = await otherInstance.getSecret('shared-name');
|
||
|
|
const originalResult = await instance.getSecret('shared-name');
|
||
|
|
|
||
|
|
expect(otherResult).toEqual('other-value');
|
||
|
|
expect(originalResult).toEqual('original-value');
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('cleanup - remove temp directory', async () => {
|
||
|
|
fs.rmSync(testDir, { recursive: true, force: true });
|
||
|
|
});
|
||
|
|
|
||
|
|
export default tap.start();
|