diff --git a/internal/model/file.go b/internal/model/file.go index a357f45..413cf0d 100644 --- a/internal/model/file.go +++ b/internal/model/file.go @@ -4,6 +4,9 @@ import ( "time" ) +// StatusUserDeleted marks a file that has been deleted by the user (soft delete). +const StatusUserDeleted = "user_deleted" + // File represents a file or directory entry in the virtual filesystem. type File struct { ID string `gorm:"primaryKey;type:varchar(36)" json:"id"` @@ -14,6 +17,7 @@ type File struct { MimeType string `gorm:"type:varchar(127)" json:"mime_type"` StoragePath string `gorm:"type:varchar(512)" json:"storage_path"` Hash string `gorm:"type:varchar(64)" json:"-"` + Status string `gorm:"type:varchar(32);not null;default:active;index" json:"-"` IsDir bool `gorm:"default:false" json:"is_dir"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` diff --git a/internal/model/file_test.go b/internal/model/file_test.go new file mode 100644 index 0000000..9ff7711 --- /dev/null +++ b/internal/model/file_test.go @@ -0,0 +1,49 @@ +package model + +import ( + "encoding/json" + "testing" + "time" +) + +func TestFile_StatusExcludedFromJSON(t *testing.T) { + f := &File{ + ID: "1", + UserID: "u1", + ParentID: nil, + Name: "test.txt", + Size: 100, + MimeType: "text/plain", + Status: StatusActive, + IsDir: false, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + data, err := json.Marshal(f) + if err != nil { + t.Fatalf("json.Marshal: %v", err) + } + + var result map[string]any + if err := json.Unmarshal(data, &result); err != nil { + t.Fatalf("json.Unmarshal: %v", err) + } + + if _, ok := result["status"]; ok { + t.Errorf("Status field should be excluded from JSON (json:\"-\"), but it appeared in output") + } + + if _, ok := result["hash"]; ok { + t.Errorf("Hash field should be excluded from JSON (json:\"-\"), but it appeared in output") + } +} + +func TestStatusConstants(t *testing.T) { + if StatusActive != "active" { + t.Errorf("StatusActive = %q, want \"active\"", StatusActive) + } + if StatusUserDeleted != "user_deleted" { + t.Errorf("StatusUserDeleted = %q, want \"user_deleted\"", StatusUserDeleted) + } +} diff --git a/internal/model/user.go b/internal/model/user.go index 9c7ee99..77673bd 100644 --- a/internal/model/user.go +++ b/internal/model/user.go @@ -11,6 +11,13 @@ type User struct { Email string `gorm:"uniqueIndex;type:varchar(255);not null" json:"email"` PasswordHash string `gorm:"type:varchar(255);not null" json:"-"` IsAdmin bool `gorm:"default:false" json:"is_admin"` + Status string `gorm:"type:varchar(32);not null;default:active;index" json:"-"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } + +// User status constants. +const ( + StatusActive = "active" + StatusAdminDeleted = "admin_deleted" +) diff --git a/internal/model/user_test.go b/internal/model/user_test.go new file mode 100644 index 0000000..e63fca6 --- /dev/null +++ b/internal/model/user_test.go @@ -0,0 +1,29 @@ +package model + +import ( + "encoding/json" + "testing" +) + +func TestUserJSONOmitsStatusField(t *testing.T) { + u := User{ + ID: "user-1", + Username: "alice", + Email: "alice@example.com", + Status: StatusActive, + } + + raw, err := json.Marshal(u) + if err != nil { + t.Fatalf("json.Marshal: %v", err) + } + + var m map[string]interface{} + if err := json.Unmarshal(raw, &m); err != nil { + t.Fatalf("json.Unmarshal: %v", err) + } + + if _, ok := m["status"]; ok { + t.Error("Status field should not appear in JSON output") + } +}