- Add upsert pattern to SecretsScanService to prevent duplicate key errors on repeated scans - Auto-test connection health on startup so status reflects reality - Fix Actions view to read identity from appstate instead of broken localStorage hack - Fetch both project and group secrets in parallel, add "All Scopes" filter to Secrets view - Enable noCache on UtilityWebsiteServer to prevent stale browser cache
144 lines
5.3 KiB
TypeScript
144 lines
5.3 KiB
TypeScript
import { assertEquals, assertExists } from 'https://deno.land/std@0.208.0/assert/mod.ts';
|
|
import { StorageManager } from '../ts/storage/index.ts';
|
|
import * as smartsecret from '@push.rocks/smartsecret';
|
|
|
|
Deno.test('StorageManager memory: set and get', async () => {
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
await sm.set('/test/key1', 'hello');
|
|
const result = await sm.get('/test/key1');
|
|
assertEquals(result, 'hello');
|
|
});
|
|
|
|
Deno.test('StorageManager memory: get nonexistent returns null', async () => {
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
const result = await sm.get('/missing');
|
|
assertEquals(result, null);
|
|
});
|
|
|
|
Deno.test('StorageManager memory: delete', async () => {
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
await sm.set('/test/key1', 'hello');
|
|
const deleted = await sm.delete('/test/key1');
|
|
assertEquals(deleted, true);
|
|
const result = await sm.get('/test/key1');
|
|
assertEquals(result, null);
|
|
});
|
|
|
|
Deno.test('StorageManager memory: delete nonexistent returns false', async () => {
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
const deleted = await sm.delete('/missing');
|
|
assertEquals(deleted, false);
|
|
});
|
|
|
|
Deno.test('StorageManager memory: exists', async () => {
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
assertEquals(await sm.exists('/test/key1'), false);
|
|
await sm.set('/test/key1', 'hello');
|
|
assertEquals(await sm.exists('/test/key1'), true);
|
|
});
|
|
|
|
Deno.test('StorageManager memory: list keys under prefix', async () => {
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
await sm.set('/connections/a.json', '{}');
|
|
await sm.set('/connections/b.json', '{}');
|
|
await sm.set('/other/c.json', '{}');
|
|
const keys = await sm.list('/connections/');
|
|
assertEquals(keys, ['/connections/a.json', '/connections/b.json']);
|
|
});
|
|
|
|
Deno.test('StorageManager memory: getJSON and setJSON roundtrip', async () => {
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
const data = { id: '123', name: 'test', nested: { value: 42 } };
|
|
await sm.setJSON('/data/item.json', data);
|
|
const result = await sm.getJSON<typeof data>('/data/item.json');
|
|
assertEquals(result, data);
|
|
});
|
|
|
|
Deno.test('StorageManager memory: getJSON nonexistent returns null', async () => {
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
const result = await sm.getJSON('/missing.json');
|
|
assertEquals(result, null);
|
|
});
|
|
|
|
Deno.test('StorageManager: key validation requires leading slash', async () => {
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
let threw = false;
|
|
try {
|
|
await sm.get('no-slash');
|
|
} catch {
|
|
threw = true;
|
|
}
|
|
assertEquals(threw, true);
|
|
});
|
|
|
|
Deno.test('StorageManager: key normalization strips ..', async () => {
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
await sm.set('/test/../actual/key', 'value');
|
|
// '..' segments are stripped, so key becomes /test/actual/key — wait,
|
|
// the normalizer filters out '..' segments entirely
|
|
// /test/../actual/key -> segments: ['test', 'actual', 'key'] (.. filtered)
|
|
const result = await sm.get('/test/actual/key');
|
|
assertEquals(result, 'value');
|
|
});
|
|
|
|
Deno.test('StorageManager filesystem: set, get, delete roundtrip', async () => {
|
|
const tmpDir = await Deno.makeTempDir();
|
|
const sm = new StorageManager({ backend: 'filesystem', fsPath: tmpDir });
|
|
try {
|
|
await sm.set('/test/file.txt', 'filesystem content');
|
|
const result = await sm.get('/test/file.txt');
|
|
assertEquals(result, 'filesystem content');
|
|
|
|
assertEquals(await sm.exists('/test/file.txt'), true);
|
|
|
|
const deleted = await sm.delete('/test/file.txt');
|
|
assertEquals(deleted, true);
|
|
assertEquals(await sm.get('/test/file.txt'), null);
|
|
} finally {
|
|
await Deno.remove(tmpDir, { recursive: true });
|
|
}
|
|
});
|
|
|
|
Deno.test('StorageManager filesystem: list keys', async () => {
|
|
const tmpDir = await Deno.makeTempDir();
|
|
const sm = new StorageManager({ backend: 'filesystem', fsPath: tmpDir });
|
|
try {
|
|
await sm.setJSON('/items/a.json', { id: 'a' });
|
|
await sm.setJSON('/items/b.json', { id: 'b' });
|
|
const keys = await sm.list('/items/');
|
|
assertEquals(keys, ['/items/a.json', '/items/b.json']);
|
|
} finally {
|
|
await Deno.remove(tmpDir, { recursive: true });
|
|
}
|
|
});
|
|
|
|
Deno.test('ConnectionManager with StorageManager: create and load', async () => {
|
|
const { ConnectionManager } = await import('../ts/classes/connectionmanager.ts');
|
|
const sm = new StorageManager({ backend: 'memory' });
|
|
const secret = new smartsecret.SmartSecret({ service: 'gitops-test' });
|
|
const cm = new ConnectionManager(sm, secret);
|
|
await cm.init();
|
|
|
|
// Create a connection
|
|
const conn = await cm.createConnection('test', 'gitea', 'https://gitea.example.com', 'token');
|
|
assertExists(conn.id);
|
|
assertEquals(conn.name, 'test');
|
|
assertEquals(conn.token, '***');
|
|
|
|
// Verify it's stored in StorageManager
|
|
const stored = await sm.getJSON<{ id: string }>(`/connections/${conn.id}.json`);
|
|
assertExists(stored);
|
|
assertEquals(stored.id, conn.id);
|
|
|
|
// Create a new ConnectionManager and verify it loads the connection
|
|
const cm2 = new ConnectionManager(sm, secret);
|
|
await cm2.init();
|
|
const conns = cm2.getConnections();
|
|
assertEquals(conns.length, 1);
|
|
assertEquals(conns[0].id, conn.id);
|
|
|
|
// Wait for background health checks to avoid resource leaks
|
|
await cm.healthCheckDone;
|
|
await cm2.healthCheckDone;
|
|
});
|