feat(storage): add StorageManager and cache subsystem; integrate storage into ConnectionManager and GitopsApp, migrate legacy connections, and add tests
This commit is contained in:
@@ -2,6 +2,7 @@ import { assertEquals, assertExists } from 'https://deno.land/std@0.208.0/assert
|
||||
import { BaseProvider, GiteaProvider, GitLabProvider } from '../ts/providers/index.ts';
|
||||
import { ConnectionManager } from '../ts/classes/connectionmanager.ts';
|
||||
import { GitopsApp } from '../ts/classes/gitopsapp.ts';
|
||||
import { StorageManager } from '../ts/storage/index.ts';
|
||||
|
||||
Deno.test('GiteaProvider instantiates correctly', () => {
|
||||
const provider = new GiteaProvider('test-id', 'https://gitea.example.com', 'test-token');
|
||||
@@ -18,13 +19,15 @@ Deno.test('GitLabProvider instantiates correctly', () => {
|
||||
});
|
||||
|
||||
Deno.test('ConnectionManager instantiates correctly', () => {
|
||||
const manager = new ConnectionManager();
|
||||
const storage = new StorageManager({ backend: 'memory' });
|
||||
const manager = new ConnectionManager(storage);
|
||||
assertExists(manager);
|
||||
});
|
||||
|
||||
Deno.test('GitopsApp instantiates correctly', () => {
|
||||
const app = new GitopsApp();
|
||||
assertExists(app);
|
||||
assertExists(app.storageManager);
|
||||
assertExists(app.connectionManager);
|
||||
assertExists(app.opsServer);
|
||||
});
|
||||
|
||||
137
test/test.storage_test.ts
Normal file
137
test/test.storage_test.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { assertEquals, assertExists } from 'https://deno.land/std@0.208.0/assert/mod.ts';
|
||||
import { StorageManager } from '../ts/storage/index.ts';
|
||||
|
||||
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 cm = new ConnectionManager(sm);
|
||||
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);
|
||||
await cm2.init();
|
||||
const conns = cm2.getConnections();
|
||||
assertEquals(conns.length, 1);
|
||||
assertEquals(conns[0].id, conn.id);
|
||||
});
|
||||
59
test/test.tsmdb_spike_test.ts
Normal file
59
test/test.tsmdb_spike_test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { assertEquals, assertExists } from 'https://deno.land/std@0.208.0/assert/mod.ts';
|
||||
import { LocalTsmDb } from '@push.rocks/smartmongo';
|
||||
import { SmartdataDb, SmartDataDbDoc, Collection, svDb, unI } from '@push.rocks/smartdata';
|
||||
|
||||
Deno.test({
|
||||
name: 'TsmDb spike: LocalTsmDb + SmartdataDb roundtrip',
|
||||
sanitizeOps: false,
|
||||
sanitizeResources: false,
|
||||
fn: async () => {
|
||||
const tmpDir = await Deno.makeTempDir();
|
||||
|
||||
// 1. Start local MongoDB-compatible server
|
||||
const localDb = new LocalTsmDb({ folderPath: tmpDir });
|
||||
const { connectionUri } = await localDb.start();
|
||||
assertExists(connectionUri);
|
||||
|
||||
// 2. Connect smartdata
|
||||
const smartDb = new SmartdataDb({
|
||||
mongoDbUrl: connectionUri,
|
||||
mongoDbName: 'gitops_spike_test',
|
||||
});
|
||||
await smartDb.init();
|
||||
assertEquals(smartDb.status, 'connected');
|
||||
|
||||
// 3. Define a simple document class
|
||||
@Collection(() => smartDb)
|
||||
class TestDoc extends SmartDataDbDoc<TestDoc, TestDoc> {
|
||||
@unI()
|
||||
public id: string = '';
|
||||
|
||||
@svDb()
|
||||
public label: string = '';
|
||||
|
||||
@svDb()
|
||||
public value: number = 0;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Insert a document
|
||||
const doc = new TestDoc();
|
||||
doc.id = 'test-1';
|
||||
doc.label = 'spike';
|
||||
doc.value = 42;
|
||||
await doc.save();
|
||||
|
||||
// 5. Query it back
|
||||
const found = await TestDoc.getInstance({ id: 'test-1' });
|
||||
assertExists(found);
|
||||
assertEquals(found.label, 'spike');
|
||||
assertEquals(found.value, 42);
|
||||
|
||||
// 6. Cleanup — smartDb closes; localDb.stop() hangs under Deno, so fire-and-forget
|
||||
await smartDb.close();
|
||||
localDb.stop().catch(() => {});
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user