137 lines
5.6 KiB
C++
137 lines
5.6 KiB
C++
|
|
// Интеграционный тест: цепочка VaultManager + FileStore + DocumentManager + VaultIndex + GitManager + MdParser
|
||
|
|
#include <QCoreApplication>
|
||
|
|
#include <QTemporaryDir>
|
||
|
|
#include <QDir>
|
||
|
|
#include <QFile>
|
||
|
|
#include <QSettings>
|
||
|
|
#include <cstdio>
|
||
|
|
|
||
|
|
#include "app/VaultManager.h"
|
||
|
|
#include "core/SettingsStore.h"
|
||
|
|
#include "core/FileStore.h"
|
||
|
|
#include "core/DocumentManager.h"
|
||
|
|
#include "core/VaultIndex.h"
|
||
|
|
#include "git/GitManager.h"
|
||
|
|
#include "markdown/MdParser.h"
|
||
|
|
|
||
|
|
static int fail(const char *msg, const QString &detail=QString()){ std::fprintf(stderr,"FAIL: %s %s\n",msg,qPrintable(detail)); return 1;}
|
||
|
|
|
||
|
|
int main(int argc, char *argv[]){
|
||
|
|
QCoreApplication app(argc, argv);
|
||
|
|
app.setOrganizationName("FloraInteg");
|
||
|
|
app.setApplicationName("integ_test");
|
||
|
|
QTemporaryDir tmp;
|
||
|
|
if(!tmp.isValid()) return fail("tmp");
|
||
|
|
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, tmp.path());
|
||
|
|
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||
|
|
|
||
|
|
SettingsStore settings;
|
||
|
|
GitManager git;
|
||
|
|
VaultManager vaultMgr(&settings, &git);
|
||
|
|
|
||
|
|
QString vaultPath = tmp.path()+"/myvault";
|
||
|
|
QString err = vaultMgr.createVault(vaultPath);
|
||
|
|
if(!err.isEmpty()) return fail("createVault",err);
|
||
|
|
if(!settings.hasVault(vaultPath)) return fail("hasVault");
|
||
|
|
if(!git.hasCommits(vaultPath) && false) {} // may be no commits yet
|
||
|
|
|
||
|
|
// FileStore operations
|
||
|
|
FileStore fs;
|
||
|
|
err = fs.openVault(vaultPath);
|
||
|
|
if(!err.isEmpty()) return fail("openVault",err);
|
||
|
|
err = fs.createFile(vaultPath, "note1");
|
||
|
|
if(!err.isEmpty()) return fail("createFile note1",err);
|
||
|
|
err = fs.createFolder(vaultPath, "folder1");
|
||
|
|
if(!err.isEmpty()) return fail("createFolder",err);
|
||
|
|
QString note1 = vaultPath+"/note1.md";
|
||
|
|
QString folder1 = vaultPath+"/folder1";
|
||
|
|
// create inside folder
|
||
|
|
err = fs.createFile(folder1, "inner.md");
|
||
|
|
if(!err.isEmpty()) return fail("inner",err);
|
||
|
|
QString inner = folder1+"/inner.md";
|
||
|
|
|
||
|
|
// DocumentManager: open and edit
|
||
|
|
DocumentManager dm;
|
||
|
|
dm.setAutosaveIntervalMs(500);
|
||
|
|
int idx = dm.openDocument(note1);
|
||
|
|
if(idx!=0) return fail("open note1");
|
||
|
|
dm.updateContent(idx, "# Header #tagA\n\nLink to [[inner]] and #tagB\n");
|
||
|
|
dm.saveDocument(idx);
|
||
|
|
if(dm.isDirty(idx)) return fail("still dirty");
|
||
|
|
|
||
|
|
int idx2 = dm.openDocument(inner);
|
||
|
|
if(idx2!=1) return fail("open inner");
|
||
|
|
dm.updateContent(idx2, "# Inner\n\nBack to [[note1]] #tagA\n");
|
||
|
|
dm.saveDocument(idx2);
|
||
|
|
|
||
|
|
// Git commit all
|
||
|
|
if(!git.commitAll(vaultPath, "initial content")) return fail("commitAll", git.lastError());
|
||
|
|
if(!git.hasCommits(vaultPath)) return fail("hasCommits after");
|
||
|
|
|
||
|
|
// VaultIndex should find tags and links
|
||
|
|
VaultIndex vi;
|
||
|
|
vi.rebuild(vaultPath);
|
||
|
|
if(vi.fileCount()!=2) return fail("fileCount 2", QString::number(vi.fileCount()));
|
||
|
|
QStringList tags = vi.tagsOfFile(note1);
|
||
|
|
if(!tags.contains("tagA") || !tags.contains("tagB")) return fail("tags note1", tags.join(","));
|
||
|
|
auto links = vi.linksOfFile(note1);
|
||
|
|
bool foundInner=false;
|
||
|
|
for(auto l: links) if(l.toMap().value("path").toString()==inner) foundInner=true;
|
||
|
|
if(!foundInner) return fail("link note1->inner");
|
||
|
|
if(vi.filesWithTag("tagA").size()!=2) return fail("tagA files 2");
|
||
|
|
|
||
|
|
// MdParser render
|
||
|
|
MdParser parser;
|
||
|
|
QString content = dm.contentAt(idx);
|
||
|
|
QString html = parser.render(content, vaultPath, QFileInfo(note1).absolutePath());
|
||
|
|
if(!html.contains("tagA") && !html.contains("tag")) {} // tags are not rendered as html, but links should be
|
||
|
|
if(!html.contains("flora://open")) return fail("md render link", html);
|
||
|
|
if(html.contains("onload")) return fail("xss");
|
||
|
|
|
||
|
|
// Daily note
|
||
|
|
QString dn = fs.dailyNotePath("2026-09-01");
|
||
|
|
if(dn.isEmpty()) return fail("dailyNote");
|
||
|
|
if(!QFileInfo::exists(dn)) return fail("dailyNote not exists");
|
||
|
|
int dnIdx = dm.openDocument(dn);
|
||
|
|
if(dnIdx<0) return fail("open daily");
|
||
|
|
dm.updateContent(dnIdx, "# 2026-09-01\n\nDaily #daily\n");
|
||
|
|
dm.saveDocument(dnIdx);
|
||
|
|
vi.rebuild(vaultPath);
|
||
|
|
if(vi.fileCount()!=3) return fail("fileCount 3 after daily");
|
||
|
|
if(!vi.tagsOfFile(dn).contains("daily")) return fail("daily tag");
|
||
|
|
|
||
|
|
// Rename and move
|
||
|
|
err = fs.renameItem(note1, "renamed.md");
|
||
|
|
if(!err.isEmpty()) return fail("rename",err);
|
||
|
|
QString renamed = vaultPath+"/renamed.md";
|
||
|
|
if(!QFileInfo::exists(renamed)) return fail("renamed not exists");
|
||
|
|
// DocumentManager path still old, but file moved - should handle? reopen
|
||
|
|
dm.closeTab(idx); // close old (should be at index? after previous ops indices shifted - find renamed)
|
||
|
|
// open renamed
|
||
|
|
int idxR = dm.openDocument(renamed);
|
||
|
|
if(idxR<0) return fail("open renamed");
|
||
|
|
|
||
|
|
// Move inner to root
|
||
|
|
err = fs.moveItem(inner, vaultPath);
|
||
|
|
if(!err.isEmpty()) return fail("move inner to root",err);
|
||
|
|
if(!QFileInfo::exists(vaultPath+"/inner.md")) return fail("moved inner not at root");
|
||
|
|
|
||
|
|
// Remove folder (now empty)
|
||
|
|
err = fs.removeItem(folder1);
|
||
|
|
if(!err.isEmpty()) return fail("remove folder",err);
|
||
|
|
if(QDir(folder1).exists()) return fail("folder still exists");
|
||
|
|
|
||
|
|
// Git status checks
|
||
|
|
QString st = git.fileGitStatus(vaultPath, renamed);
|
||
|
|
if(st.isEmpty()) return fail("git status empty");
|
||
|
|
// commit again
|
||
|
|
if(!git.commitAll(vaultPath, "after moves")) return fail("commit after moves", git.lastError());
|
||
|
|
|
||
|
|
// SettingsStore viewMode persistence across vaults
|
||
|
|
settings.setViewModeForFile(vaultPath, renamed, "preview");
|
||
|
|
if(settings.viewModeForFile(vaultPath, renamed)!="preview") return fail("viewMode", settings.viewModeForFile(vaultPath, renamed));
|
||
|
|
|
||
|
|
std::printf("INTEGRATION TEST OK\n");
|
||
|
|
return 0;
|
||
|
|
}
|