feat(v030): SyncService.sync — 양방향 6단계 (export/commit/fetch/rebase/re-import/push) + conflict 반환

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
altair823
2026-05-10 03:40:09 +09:00
parent 9a1f0e269a
commit 33588b09df
6 changed files with 207 additions and 23 deletions

View File

@@ -9,6 +9,7 @@ import { runMigrations } from '@main/db/migrations/index.js';
import { NoteRepository } from '@main/repository/NoteRepository.js';
import { MediaStore } from '@main/services/MediaStore.js';
import { ExportService } from '@main/services/ExportService.js';
import { ImportService } from '@main/services/ImportService.js';
import { SyncService } from '@main/services/SyncService.js';
const execFileAsync = promisify(execFile);
@@ -47,6 +48,7 @@ describe('SyncService', () => {
let repo: NoteRepository;
let mediaStore: MediaStore;
let exportSvc: ExportService;
let importSvc: ImportService;
let svc: SyncService;
let remoteDir: string | null = null;
let prevEnv: NodeJS.ProcessEnv;
@@ -73,7 +75,8 @@ describe('SyncService', () => {
repo = new NoteRepository(db);
mediaStore = new MediaStore(profileDir);
exportSvc = new ExportService(repo, mediaStore, () => new Date('2026-04-26T12:00:00Z'));
svc = new SyncService(profileDir, exportSvc, () => new Date('2026-04-26T12:00:00Z'));
importSvc = new ImportService(repo, mediaStore);
svc = new SyncService(profileDir, exportSvc, importSvc, () => new Date('2026-04-26T12:00:00Z'));
});
afterEach(() => {
@@ -110,7 +113,7 @@ describe('SyncService', () => {
expect(r.ok).toBe(true);
expect(r.changed).toBe(true);
expect(r.pushed).toBe(true);
expect(r.sha).toMatch(/^[0-9a-f]{40}$/);
expect(r.localSha).toMatch(/^[0-9a-f]{40}$/);
expect(existsSync(join(svc.getSyncDir(), 'manifest.json'))).toBe(true);
expect(existsSync(join(svc.getSyncDir(), 'notes'))).toBe(true);
expect(existsSync(join(svc.getSyncDir(), 'index.jsonl'))).toBe(true);
@@ -122,10 +125,11 @@ describe('SyncService', () => {
const first = await svc.sync();
expect(first.ok).toBe(true);
expect(first.changed).toBe(true);
// Re-sync without DB change. With fixed now() → identical files → git sees no change.
// Re-sync without DB change. With fixed now() → identical files → git sees no local change.
// New bidirectional flow: always does fetch+rebase+re-import+push.
const second = await svc.sync();
expect(second.ok).toBe(true);
expect(second.changed).toBe(false);
expect(second.pushed).toBe(false);
expect(second.changed).toBe(false); // no local commit + importedCount=0
expect(second.pushed).toBe(true); // push always runs on success
});
});