package files import ( "os" "path/filepath" "testing" "verstak/internal/core/storage" ) func openTestDB(t *testing.T) *storage.DB { t.Helper() dir := t.TempDir() db, err := storage.Open(filepath.Join(dir, "test.db")) if err != nil { t.Fatalf("open db: %v", err) } t.Cleanup(func() { db.Close() }) return db } func TestAddExternal(t *testing.T) { db := openTestDB(t) // Run migration 002 manually since storage.Open already applied it. // We can verify the table exists by inserting. filesSvc := NewService(db, t.TempDir()) // Create a real temp file to register. tmpDir := t.TempDir() tmpFile := filepath.Join(tmpDir, "test.txt") if err := os.WriteFile(tmpFile, []byte("hello world"), 0o640); err != nil { t.Fatal(err) } rec, err := filesSvc.AddExternal("node-1", tmpFile) if err != nil { t.Fatalf("AddExternal: %v", err) } if rec.ID == "" { t.Fatal("empty id") } if rec.Filename != "test.txt" { t.Errorf("filename = %q", rec.Filename) } if rec.StorageMode != "external" { t.Errorf("mode = %q", rec.StorageMode) } if rec.Size != 11 { t.Errorf("size = %d, want 11", rec.Size) } // Verify stored. got, err := filesSvc.Get(rec.ID) if err != nil { t.Fatal(err) } if got.Filename != "test.txt" { t.Errorf("got filename = %q", got.Filename) } } func TestCopyIntoVault(t *testing.T) { db := openTestDB(t) vaultRoot := t.TempDir() svc := NewService(db, vaultRoot) // Source file. srcDir := t.TempDir() srcFile := filepath.Join(srcDir, "doc.pdf") os.WriteFile(srcFile, []byte("PDF content here"), 0o640) rec, err := svc.CopyIntoVault("node-1", srcFile, "my-node") if err != nil { t.Fatalf("CopyIntoVault: %v", err) } if rec.SHA256 == "" { t.Error("expected sha256") } if rec.StorageMode != "vault" { t.Errorf("mode = %q", rec.StorageMode) } // Verify file on disk. if _, err := os.Stat(filepath.Join(vaultRoot, rec.Path)); err != nil { t.Errorf("file on disk: %v", err) } } func TestListByNode(t *testing.T) { db := openTestDB(t) svc := NewService(db, t.TempDir()) os.WriteFile(filepath.Join(t.TempDir(), "a.txt"), []byte("a"), 0o640) f1 := filepath.Join(t.TempDir(), "a1.txt") f2 := filepath.Join(t.TempDir(), "a2.txt") os.WriteFile(f1, []byte("a"), 0o640) os.WriteFile(f2, []byte("bb"), 0o640) svc.AddExternal("node-a", f1) svc.AddExternal("node-a", f2) list, err := svc.ListByNode("node-a") if err != nil { t.Fatal(err) } if len(list) != 2 { t.Errorf("list len = %d, want 2", len(list)) } } func TestDeleteToTrash(t *testing.T) { db := openTestDB(t) vaultRoot := t.TempDir() svc := NewService(db, vaultRoot) src := filepath.Join(t.TempDir(), "important.pdf") os.WriteFile(src, []byte("important data"), 0o640) rec, _ := svc.CopyIntoVault("node-x", src, "node-x") if err := svc.DeleteToTrash(rec.ID); err != nil { t.Fatalf("DeleteToTrash: %v", err) } // File record should be gone. if _, err := svc.Get(rec.ID); err == nil { t.Error("expected error after trash") } // Original file should not exist anymore (moved to trash). if _, err := os.Stat(filepath.Join(vaultRoot, rec.Path)); !os.IsNotExist(err) { t.Error("expected file to be moved from original location") } // Trash dir should have it. trashDir := filepath.Join(vaultRoot, ".verstak", "trash") entries, _ := os.ReadDir(trashDir) if len(entries) != 1 { t.Errorf("trash entries = %d, want 1", len(entries)) } } func TestGuessMIME(t *testing.T) { cases := map[string]string{ "a.md": "text/plain", "a.png": "image/png", "a.pdf": "application/pdf", "a.docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "a.go": "text/plain", "a.unknown": "application/octet-stream", } for name, want := range cases { got := guessMIME(name) if got != want { t.Errorf("guessMIME(%q) = %q, want %q", name, got, want) } } }