mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-02 19:58:51 +08:00
✨ feat(sql-audit): 新增 SQL 审计中心并完善事务追踪
- 新增脱敏审计存储、筛选、保留策略、完整性校验与 JSON/CSV 导出 - 覆盖查询编辑器、事务、导入同步、对象操作、AI/MCP 与 Web 运行时入口 - 完善事务完整语句日志、Windows 快捷键映射及 SQL 分析布局 - 补充多语言、健康状态、数据目录迁移和回归测试
This commit is contained in:
@@ -5,10 +5,38 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"GoNavi-Wails/internal/appdata"
|
||||
"GoNavi-Wails/internal/connection"
|
||||
)
|
||||
|
||||
func TestApplyDataRootDirectorySerializesConcurrentRequests(t *testing.T) {
|
||||
app := NewApp()
|
||||
app.dataRootApplyMu.Lock()
|
||||
done := make(chan connection.QueryResult, 1)
|
||||
go func() {
|
||||
done <- app.ApplyDataRootDirectory(appdata.MustResolveActiveRoot(), false)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
app.dataRootApplyMu.Unlock()
|
||||
t.Fatal("ApplyDataRootDirectory bypassed the application-wide serialization lock")
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
app.dataRootApplyMu.Unlock()
|
||||
|
||||
select {
|
||||
case result := <-done:
|
||||
if !result.Success {
|
||||
t.Fatalf("serialized ApplyDataRootDirectory returned failure: %s", result.Message)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("serialized ApplyDataRootDirectory did not resume")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateDataRootContentsCopiesKnownFilesAndDirectories(t *testing.T) {
|
||||
sourceRoot := t.TempDir()
|
||||
targetRoot := filepath.Join(t.TempDir(), "gonavi-data")
|
||||
@@ -46,6 +74,64 @@ func TestMigrateDataRootContentsCopiesKnownFilesAndDirectories(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateDataRootContentsReplacesAuditDirectoryWithoutStaleSidecars(t *testing.T) {
|
||||
sourceRoot := t.TempDir()
|
||||
targetRoot := t.TempDir()
|
||||
sourceAudit := filepath.Join(sourceRoot, "audit")
|
||||
targetAudit := filepath.Join(targetRoot, "audit")
|
||||
if err := os.MkdirAll(sourceAudit, 0o700); err != nil {
|
||||
t.Fatalf("create source audit directory: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll(targetAudit, 0o700); err != nil {
|
||||
t.Fatalf("create target audit directory: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(sourceAudit, "sql_audit.db"), []byte("source-db"), 0o600); err != nil {
|
||||
t.Fatalf("write source audit database: %v", err)
|
||||
}
|
||||
for name, content := range map[string]string{
|
||||
"sql_audit.db": "old-db",
|
||||
"sql_audit.db-wal": "stale-wal",
|
||||
"sql_audit.db-shm": "stale-shm",
|
||||
"sql_audit_health.json": "stale-health",
|
||||
} {
|
||||
if err := os.WriteFile(filepath.Join(targetAudit, name), []byte(content), 0o600); err != nil {
|
||||
t.Fatalf("write target audit artifact %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := migrateDataRootContents(sourceRoot, targetRoot); err != nil {
|
||||
t.Fatalf("migrateDataRootContents returned error: %v", err)
|
||||
}
|
||||
payload, err := os.ReadFile(filepath.Join(targetAudit, "sql_audit.db"))
|
||||
if err != nil || string(payload) != "source-db" {
|
||||
t.Fatalf("target audit database = %q err=%v, want exact source snapshot", payload, err)
|
||||
}
|
||||
for _, name := range []string{"sql_audit.db-wal", "sql_audit.db-shm", "sql_audit_health.json"} {
|
||||
if _, err := os.Stat(filepath.Join(targetAudit, name)); !os.IsNotExist(err) {
|
||||
t.Fatalf("stale target audit artifact %s survived exact replacement: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateDataRootContentsRemovesTargetAuditWhenSourceHasNone(t *testing.T) {
|
||||
sourceRoot := t.TempDir()
|
||||
targetRoot := t.TempDir()
|
||||
targetAudit := filepath.Join(targetRoot, "audit")
|
||||
if err := os.MkdirAll(targetAudit, 0o700); err != nil {
|
||||
t.Fatalf("create target audit directory: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(targetAudit, "sql_audit.db-wal"), []byte("stale"), 0o600); err != nil {
|
||||
t.Fatalf("write stale target audit artifact: %v", err)
|
||||
}
|
||||
|
||||
if err := migrateDataRootContents(sourceRoot, targetRoot); err != nil {
|
||||
t.Fatalf("migrateDataRootContents returned error: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(targetAudit); !os.IsNotExist(err) {
|
||||
t.Fatalf("target audit directory survived despite absent source snapshot: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateDataRootContentsCopiesSecurityUpdateStateAndRewritesBackupPaths(t *testing.T) {
|
||||
sourceRoot := t.TempDir()
|
||||
targetRoot := filepath.Join(t.TempDir(), "gonavi-data")
|
||||
|
||||
Reference in New Issue
Block a user