mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-10 16:53:35 +08:00
- 将日志目录配置整合到数据目录设置,支持选择、打开、恢复默认与保存 - 持久化日志目录并保留环境变量优先级,目录调整后重启生效 - 通过跨进程锁和原子写入保护数据目录与日志目录配置 - 移除重复标题并互斥目录修改操作,补齐六语言文案 - 补充桌面接口、浏览器模拟及并发回归测试
233 lines
7.3 KiB
Go
233 lines
7.3 KiB
Go
package appdata
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestResolveActiveRootDefaultsToLegacyGonaviDir(t *testing.T) {
|
|
homeDir := t.TempDir()
|
|
t.Setenv("HOME", homeDir)
|
|
t.Setenv("USERPROFILE", homeDir)
|
|
|
|
root, err := ResolveActiveRoot()
|
|
if err != nil {
|
|
t.Fatalf("ResolveActiveRoot returned error: %v", err)
|
|
}
|
|
expected := filepath.Join(homeDir, ".gonavi")
|
|
if root != expected {
|
|
t.Fatalf("expected default root %q, got %q", expected, root)
|
|
}
|
|
}
|
|
|
|
func TestSetActiveRootWritesBootstrapAndResolveUsesIt(t *testing.T) {
|
|
homeDir := t.TempDir()
|
|
t.Setenv("HOME", homeDir)
|
|
t.Setenv("USERPROFILE", homeDir)
|
|
|
|
customRoot := filepath.Join(t.TempDir(), "gonavi-data")
|
|
savedRoot, err := SetActiveRoot(customRoot)
|
|
if err != nil {
|
|
t.Fatalf("SetActiveRoot returned error: %v", err)
|
|
}
|
|
if savedRoot != customRoot {
|
|
t.Fatalf("expected saved root %q, got %q", customRoot, savedRoot)
|
|
}
|
|
if _, err := os.Stat(BootstrapPath()); err != nil {
|
|
t.Fatalf("expected bootstrap file to exist: %v", err)
|
|
}
|
|
resolvedRoot, err := ResolveActiveRoot()
|
|
if err != nil {
|
|
t.Fatalf("ResolveActiveRoot returned error: %v", err)
|
|
}
|
|
if resolvedRoot != customRoot {
|
|
t.Fatalf("expected custom root %q, got %q", customRoot, resolvedRoot)
|
|
}
|
|
}
|
|
|
|
func TestSetActiveRootResetToDefaultRemovesBootstrap(t *testing.T) {
|
|
homeDir := t.TempDir()
|
|
t.Setenv("HOME", homeDir)
|
|
t.Setenv("USERPROFILE", homeDir)
|
|
|
|
customRoot := filepath.Join(t.TempDir(), "gonavi-data")
|
|
if _, err := SetActiveRoot(customRoot); err != nil {
|
|
t.Fatalf("SetActiveRoot custom returned error: %v", err)
|
|
}
|
|
defaultRoot, err := SetActiveRoot("")
|
|
if err != nil {
|
|
t.Fatalf("SetActiveRoot default returned error: %v", err)
|
|
}
|
|
expectedDefault := filepath.Join(homeDir, ".gonavi")
|
|
if defaultRoot != expectedDefault {
|
|
t.Fatalf("expected default root %q, got %q", expectedDefault, defaultRoot)
|
|
}
|
|
if _, err := os.Stat(BootstrapPath()); !os.IsNotExist(err) {
|
|
t.Fatalf("expected bootstrap file to be removed, got err=%v", err)
|
|
}
|
|
}
|
|
|
|
func TestResolveActiveRootPrefersEnvOverride(t *testing.T) {
|
|
homeDir := t.TempDir()
|
|
t.Setenv("HOME", homeDir)
|
|
t.Setenv("USERPROFILE", homeDir)
|
|
|
|
customRoot := filepath.Join(t.TempDir(), "custom-root")
|
|
if _, err := SetActiveRoot(customRoot); err != nil {
|
|
t.Fatalf("SetActiveRoot returned error: %v", err)
|
|
}
|
|
|
|
overrideRoot := filepath.Join(t.TempDir(), "override-root")
|
|
t.Setenv(dataRootEnvName, overrideRoot)
|
|
|
|
resolvedRoot, err := ResolveActiveRoot()
|
|
if err != nil {
|
|
t.Fatalf("ResolveActiveRoot returned error: %v", err)
|
|
}
|
|
if resolvedRoot != overrideRoot {
|
|
t.Fatalf("expected env override root %q, got %q", overrideRoot, resolvedRoot)
|
|
}
|
|
}
|
|
|
|
func TestDataRootAndLogDirectoryPreserveEachOtherInBootstrap(t *testing.T) {
|
|
homeDir := t.TempDir()
|
|
t.Setenv("HOME", homeDir)
|
|
t.Setenv("USERPROFILE", homeDir)
|
|
|
|
customDataRoot := filepath.Join(t.TempDir(), "gonavi-data")
|
|
customLogDirectory := filepath.Join(t.TempDir(), "gonavi-logs")
|
|
if _, err := SetConfiguredLogDirectory(customLogDirectory); err != nil {
|
|
t.Fatalf("SetConfiguredLogDirectory returned error: %v", err)
|
|
}
|
|
if _, err := SetActiveRoot(customDataRoot); err != nil {
|
|
t.Fatalf("SetActiveRoot returned error: %v", err)
|
|
}
|
|
|
|
resolvedLogDirectory, err := ResolveConfiguredLogDirectory()
|
|
if err != nil {
|
|
t.Fatalf("ResolveConfiguredLogDirectory returned error: %v", err)
|
|
}
|
|
if resolvedLogDirectory != customLogDirectory {
|
|
t.Fatalf("expected custom log directory %q, got %q", customLogDirectory, resolvedLogDirectory)
|
|
}
|
|
|
|
if _, err := SetActiveRoot(""); err != nil {
|
|
t.Fatalf("reset SetActiveRoot returned error: %v", err)
|
|
}
|
|
if _, err := os.Stat(BootstrapPath()); err != nil {
|
|
t.Fatalf("bootstrap should remain while log directory is customized: %v", err)
|
|
}
|
|
resolvedLogDirectory, err = ResolveConfiguredLogDirectory()
|
|
if err != nil || resolvedLogDirectory != customLogDirectory {
|
|
t.Fatalf("log directory after data-root reset = %q, %v", resolvedLogDirectory, err)
|
|
}
|
|
|
|
if _, err := SetActiveRoot(customDataRoot); err != nil {
|
|
t.Fatalf("restore custom data root returned error: %v", err)
|
|
}
|
|
if _, err := SetConfiguredLogDirectory(""); err != nil {
|
|
t.Fatalf("reset SetConfiguredLogDirectory returned error: %v", err)
|
|
}
|
|
resolvedRoot, err := ResolveActiveRoot()
|
|
if err != nil || resolvedRoot != customDataRoot {
|
|
t.Fatalf("data root after log reset = %q, %v", resolvedRoot, err)
|
|
}
|
|
if _, err := os.Stat(BootstrapPath()); err != nil {
|
|
t.Fatalf("bootstrap should remain while data root is customized: %v", err)
|
|
}
|
|
|
|
if _, err := SetActiveRoot(""); err != nil {
|
|
t.Fatalf("final SetActiveRoot reset returned error: %v", err)
|
|
}
|
|
if _, err := os.Stat(BootstrapPath()); !os.IsNotExist(err) {
|
|
t.Fatalf("bootstrap should be removed when both settings use defaults, got err=%v", err)
|
|
}
|
|
}
|
|
|
|
func TestSetConfiguredLogDirectoryRejectsFilePathWithoutChangingConfig(t *testing.T) {
|
|
homeDir := t.TempDir()
|
|
t.Setenv("HOME", homeDir)
|
|
t.Setenv("USERPROFILE", homeDir)
|
|
|
|
blockingPath := filepath.Join(t.TempDir(), "not-a-directory")
|
|
if err := os.WriteFile(blockingPath, []byte("blocked"), 0o644); err != nil {
|
|
t.Fatalf("write blocking file: %v", err)
|
|
}
|
|
if _, err := SetConfiguredLogDirectory(blockingPath); err == nil {
|
|
t.Fatal("expected file path to be rejected as a log directory")
|
|
}
|
|
configured, err := ResolveConfiguredLogDirectory()
|
|
if err != nil {
|
|
t.Fatalf("ResolveConfiguredLogDirectory returned error: %v", err)
|
|
}
|
|
if configured != "" {
|
|
t.Fatalf("failed update changed configured log directory to %q", configured)
|
|
}
|
|
}
|
|
|
|
func TestSetConfiguredLogDirectoryRejectsDirectoryAtLogFilePathWithoutChangingConfig(t *testing.T) {
|
|
homeDir := t.TempDir()
|
|
t.Setenv("HOME", homeDir)
|
|
t.Setenv("USERPROFILE", homeDir)
|
|
|
|
targetDirectory := filepath.Join(t.TempDir(), "logs")
|
|
if err := os.MkdirAll(filepath.Join(targetDirectory, configuredLogFileName), 0o755); err != nil {
|
|
t.Fatalf("create blocking log-file directory: %v", err)
|
|
}
|
|
if _, err := SetConfiguredLogDirectory(targetDirectory); err == nil {
|
|
t.Fatal("expected a directory at the log file path to be rejected")
|
|
}
|
|
configured, err := ResolveConfiguredLogDirectory()
|
|
if err != nil {
|
|
t.Fatalf("ResolveConfiguredLogDirectory returned error: %v", err)
|
|
}
|
|
if configured != "" {
|
|
t.Fatalf("failed update changed configured log directory to %q", configured)
|
|
}
|
|
}
|
|
|
|
func TestBootstrapFileLockSerializesAccess(t *testing.T) {
|
|
lockPath := filepath.Join(t.TempDir(), "storage_root.json.lock")
|
|
first, err := acquireBootstrapFileLock(lockPath)
|
|
if err != nil {
|
|
t.Fatalf("acquire first bootstrap lock: %v", err)
|
|
}
|
|
defer first.Close()
|
|
|
|
acquired := make(chan *bootstrapFileLock, 1)
|
|
errs := make(chan error, 1)
|
|
go func() {
|
|
second, err := acquireBootstrapFileLock(lockPath)
|
|
if err != nil {
|
|
errs <- err
|
|
return
|
|
}
|
|
acquired <- second
|
|
}()
|
|
|
|
select {
|
|
case second := <-acquired:
|
|
_ = second.Close()
|
|
t.Fatal("second bootstrap lock acquired before the first was released")
|
|
case err := <-errs:
|
|
t.Fatalf("acquire second bootstrap lock: %v", err)
|
|
case <-time.After(50 * time.Millisecond):
|
|
}
|
|
if err := first.Close(); err != nil {
|
|
t.Fatalf("release first bootstrap lock: %v", err)
|
|
}
|
|
|
|
select {
|
|
case second := <-acquired:
|
|
if err := second.Close(); err != nil {
|
|
t.Fatalf("release second bootstrap lock: %v", err)
|
|
}
|
|
case err := <-errs:
|
|
t.Fatalf("acquire second bootstrap lock after release: %v", err)
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("second bootstrap lock did not acquire after the first was released")
|
|
}
|
|
}
|