feat: publish prefereces stored in filesystem

This commit is contained in:
2026-02-27 14:50:20 +01:00
parent ed0551be27
commit b1e9275580
10 changed files with 466 additions and 11 deletions

View File

@@ -1076,6 +1076,148 @@ describe('MetaEngine', () => {
expect(collectTagsSpy).toHaveBeenCalledTimes(1);
});
it('should normalize sshMode to scp when publishing.json has invalid sshMode', async () => {
const metaDir = metaEngine.getMetaDir();
mockFiles.set(normalizePath(`${metaDir}/project.json`), JSON.stringify({
name: 'Project',
}));
mockFiles.set(normalizePath(`${metaDir}/publishing.json`), JSON.stringify({
sshHost: 'example.com',
sshUser: 'deploy',
sshRemotePath: '/var/www',
sshMode: 'invalid-mode',
}));
await metaEngine.syncOnStartup();
const prefs = await metaEngine.getPublishingPreferences();
expect(prefs?.sshMode).toBe('scp');
});
});
describe('Publishing Preferences', () => {
beforeEach(async () => {
await metaEngine.syncOnStartup();
});
it('should save and load publishing preferences', async () => {
await metaEngine.setPublishingPreferences({
sshHost: 'myserver.com',
sshUser: 'webmaster',
sshRemotePath: '/srv/blog',
sshMode: 'rsync',
});
const prefs = await metaEngine.getPublishingPreferences();
expect(prefs).toEqual({
sshHost: 'myserver.com',
sshUser: 'webmaster',
sshRemotePath: '/srv/blog',
sshMode: 'rsync',
});
});
it('should persist publishing preferences to meta/publishing.json', async () => {
const metaDir = metaEngine.getMetaDir();
await metaEngine.setPublishingPreferences({
sshHost: 'host.example.com',
sshUser: 'user',
sshRemotePath: '/var/www',
sshMode: 'scp',
});
const publishingPath = normalizePath(`${metaDir}/publishing.json`);
expect(mockFiles.has(publishingPath)).toBe(true);
const parsed = JSON.parse(mockFiles.get(publishingPath)!);
expect(parsed.sshHost).toBe('host.example.com');
expect(parsed.sshUser).toBe('user');
expect(parsed.sshRemotePath).toBe('/var/www');
expect(parsed.sshMode).toBe('scp');
});
it('should clear publishing preferences by removing the file', async () => {
const metaDir = metaEngine.getMetaDir();
await metaEngine.setPublishingPreferences({
sshHost: 'example.com',
sshUser: 'user',
sshRemotePath: '/var/www',
sshMode: 'scp',
});
await metaEngine.clearPublishingPreferences();
const prefs = await metaEngine.getPublishingPreferences();
expect(prefs).toBeNull();
const publishingPath = normalizePath(`${metaDir}/publishing.json`);
expect(mockFiles.has(publishingPath)).toBe(false);
});
it('should trim whitespace from string fields', async () => {
await metaEngine.setPublishingPreferences({
sshHost: ' example.com ',
sshUser: ' user ',
sshRemotePath: ' /var/www ',
sshMode: 'rsync',
});
const prefs = await metaEngine.getPublishingPreferences();
expect(prefs?.sshHost).toBe('example.com');
expect(prefs?.sshUser).toBe('user');
expect(prefs?.sshRemotePath).toBe('/var/www');
});
it('should default sshMode to scp when invalid', async () => {
await metaEngine.setPublishingPreferences({
sshHost: 'example.com',
sshUser: 'user',
sshRemotePath: '/var/www',
sshMode: 'invalid' as any,
});
const prefs = await metaEngine.getPublishingPreferences();
expect(prefs?.sshMode).toBe('scp');
});
it('should emit publishingPreferencesChanged event on set', async () => {
const listener = vi.fn();
metaEngine.on('publishingPreferencesChanged', listener);
await metaEngine.setPublishingPreferences({
sshHost: 'example.com',
sshUser: 'user',
sshRemotePath: '/var/www',
sshMode: 'scp',
});
expect(listener).toHaveBeenCalledWith({
sshHost: 'example.com',
sshUser: 'user',
sshRemotePath: '/var/www',
sshMode: 'scp',
});
});
it('should emit publishingPreferencesChanged with null on clear', async () => {
await metaEngine.setPublishingPreferences({
sshHost: 'example.com',
sshUser: 'user',
sshRemotePath: '/var/www',
sshMode: 'scp',
});
const listener = vi.fn();
metaEngine.on('publishingPreferencesChanged', listener);
await metaEngine.clearPublishingPreferences();
expect(listener).toHaveBeenCalledWith(null);
});
});
describe('Sync on Startup (continued)', () => {
it('should use custom dataDir when provided in setProjectContext', () => {
const customDataDir = path.join('custom', 'data', 'path');
metaEngine.setProjectContext('project-with-custom-dir', customDataDir);
@@ -1084,6 +1226,49 @@ describe('MetaEngine', () => {
expect(normalizePath(metaDir)).toContain(normalizePath(customDataDir));
});
it('should load publishing preferences from publishing.json during syncOnStartup', async () => {
const metaDir = metaEngine.getMetaDir();
mockFiles.set(normalizePath(`${metaDir}/project.json`), JSON.stringify({
name: 'Synced Project',
}));
mockFiles.set(normalizePath(`${metaDir}/publishing.json`), JSON.stringify({
sshHost: 'example.com',
sshUser: 'deploy',
sshRemotePath: '/var/www/blog',
sshMode: 'rsync',
}));
await metaEngine.syncOnStartup();
const prefs = await metaEngine.getPublishingPreferences();
expect(prefs).toEqual({
sshHost: 'example.com',
sshUser: 'deploy',
sshRemotePath: '/var/www/blog',
sshMode: 'rsync',
});
});
it('should return null publishing preferences when publishing.json does not exist', async () => {
await metaEngine.syncOnStartup();
const prefs = await metaEngine.getPublishingPreferences();
expect(prefs).toBeNull();
});
it('should handle malformed publishing.json gracefully during syncOnStartup', async () => {
const metaDir = metaEngine.getMetaDir();
mockFiles.set(normalizePath(`${metaDir}/project.json`), JSON.stringify({
name: 'Synced Project',
}));
mockFiles.set(normalizePath(`${metaDir}/publishing.json`), '{"sshHost":');
await expect(metaEngine.syncOnStartup()).resolves.toBeUndefined();
const prefs = await metaEngine.getPublishingPreferences();
expect(prefs).toBeNull();
});
it('should ignore and remove dataPath from project.json during syncOnStartup', async () => {
const metaDir = metaEngine.getMetaDir();
const oldPath = path.join('old', 'path', 'from', 'file');