package fs import ( "context" "os" "path/filepath" "testing" "codeberg.org/VARASYS/ZDDC/zddc/internal/zddc" ) func setupTreeRoot(t *testing.T) string { t.Helper() root := t.TempDir() // Permissive root .zddc so subdirectory ACL checks pass. if err := os.WriteFile(filepath.Join(root, ".zddc"), []byte("acl:\n permissions:\n \"*@example.com\": rwcda\n"), 0o644); err != nil { t.Fatal(err) } zddc.InvalidateCache(root) return root } func TestListDirectory_VirtualUserHome_AppearsWhenMissing(t *testing.T) { root := setupTreeRoot(t) if err := os.MkdirAll(filepath.Join(root, "Proj", "working"), 0o755); err != nil { t.Fatal(err) } zddc.InvalidateCache(root) got, err := ListDirectory(context.Background(), nil, root, "Proj/working", "alice@example.com", "/Proj/working/") if err != nil { t.Fatalf("list: %v", err) } var virtual *string for i := range got { if got[i].Virtual { n := got[i].Name virtual = &n } } if virtual == nil { t.Fatalf("expected synthetic / entry, got entries: %+v", got) } if *virtual != "alice@example.com/" { t.Errorf("synthetic name = %q, want alice@example.com/", *virtual) } } func TestListDirectory_VirtualUserHome_SuppressedWhenRealExists(t *testing.T) { root := setupTreeRoot(t) // A real folder exists for the viewer (any case). if err := os.MkdirAll(filepath.Join(root, "Proj", "working", "Alice@Example.com"), 0o755); err != nil { t.Fatal(err) } zddc.InvalidateCache(root) got, err := ListDirectory(context.Background(), nil, root, "Proj/working", "alice@example.com", "/Proj/working/") if err != nil { t.Fatalf("list: %v", err) } for _, fi := range got { if fi.Virtual { t.Errorf("synthetic entry should be suppressed when a case-fold match exists; got %+v", fi) } } } func TestListDirectory_VirtualUserHome_AnonymousNoEntry(t *testing.T) { root := setupTreeRoot(t) if err := os.MkdirAll(filepath.Join(root, "Proj", "working"), 0o755); err != nil { t.Fatal(err) } zddc.InvalidateCache(root) got, err := ListDirectory(context.Background(), nil, root, "Proj/working", "" /* no viewer */, "/Proj/working/") if err != nil { t.Fatalf("list: %v", err) } for _, fi := range got { if fi.Virtual { t.Errorf("anonymous viewer should not see synthetic entries; got %+v", fi) } } } func TestListDirectory_VirtualUserHome_OutsideWorkingNoEntry(t *testing.T) { root := setupTreeRoot(t) if err := os.MkdirAll(filepath.Join(root, "Proj", "staging"), 0o755); err != nil { t.Fatal(err) } zddc.InvalidateCache(root) got, err := ListDirectory(context.Background(), nil, root, "Proj/staging", "alice@example.com", "/Proj/staging/") if err != nil { t.Fatalf("list: %v", err) } for _, fi := range got { if fi.Virtual { t.Errorf("staging/ should not have a synthetic user-home entry; got %+v", fi) } } } func TestListDirectory_VirtualUserHome_DeepWorkingNoEntry(t *testing.T) { root := setupTreeRoot(t) // Listing inside working/ at depth 3+ — no synthetic entry should fire. if err := os.MkdirAll(filepath.Join(root, "Proj", "working", "alice@example.com"), 0o755); err != nil { t.Fatal(err) } zddc.InvalidateCache(root) got, err := ListDirectory(context.Background(), nil, root, "Proj/working/alice@example.com", "alice@example.com", "/Proj/working/alice@example.com/") if err != nil { t.Fatalf("list: %v", err) } for _, fi := range got { if fi.Virtual { t.Errorf("nested working/ subdir must not synthesise the user home; got %+v", fi) } } } func TestListDirectory_VirtualUserHome_CaseFoldWorking(t *testing.T) { root := setupTreeRoot(t) // Pre-existing PascalCase Working/. if err := os.MkdirAll(filepath.Join(root, "Proj", "Working"), 0o755); err != nil { t.Fatal(err) } zddc.InvalidateCache(root) got, err := ListDirectory(context.Background(), nil, root, "Proj/Working", "alice@example.com", "/Proj/Working/") if err != nil { t.Fatalf("list: %v", err) } var found bool for _, fi := range got { if fi.Virtual { found = true } } if !found { t.Errorf("PascalCase Working/ should still surface the synthetic entry; got entries: %+v", got) } }