From de2c9a6d0ce486d806a54503cec38b57df8001ad Mon Sep 17 00:00:00 2001 From: LordMathis Date: Tue, 19 Nov 2024 21:44:06 +0100 Subject: [PATCH] Implement files test --- server/internal/storage/files_test.go | 415 +++++++++++++++++++++++--- 1 file changed, 375 insertions(+), 40 deletions(-) diff --git a/server/internal/storage/files_test.go b/server/internal/storage/files_test.go index 39e0c6f..55e1f44 100644 --- a/server/internal/storage/files_test.go +++ b/server/internal/storage/files_test.go @@ -1,60 +1,395 @@ -// Package storage_test provides tests for the storage package. package storage_test import ( + "io/fs" "novamd/internal/storage" - "novamd/internal/testutils" + "path/filepath" "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -func TestListFilesRecursively(t *testing.T) { - tests := []testutils.TestCase{ +// TestFileNode ensures FileNode structs are created correctly +func TestFileNode(t *testing.T) { + testCases := []struct { + name string // name of the test case + node storage.FileNode + want storage.FileNode + }{ { - Name: "empty workspace returns empty list", - Setup: func(t *testing.T, fixtures any) { - fs := fixtures.(*MapFS) - require.NoError(t, fs.MkdirAll("/test/root/1/1", 0755)) + name: "file without children", + node: storage.FileNode{ + ID: "test.md", + Name: "test.md", + Path: "test.md", }, - Fixtures: NewMapFS(), - Validate: func(t *testing.T, result any, err error) { - require.NoError(t, err) - files := result.([]storage.FileNode) - assert.Empty(t, files) + want: storage.FileNode{ + ID: "test.md", + Name: "test.md", + Path: "test.md", }, }, { - Name: "lists files and directories correctly", - Setup: func(t *testing.T, fixtures any) { - fs := fixtures.(*MapFS) - err := fs.WriteFile("/test/root/1/1/file1.md", []byte("content1"), 0644) - require.NoError(t, err, "Failed to write file1.md") - - err = fs.WriteFile("/test/root/1/1/dir/file2.md", []byte("content2"), 0644) - require.NoError(t, err, "Failed to write file2.md") + name: "directory with children", + node: storage.FileNode{ + ID: "dir", + Name: "dir", + Path: "dir", + Children: []storage.FileNode{ + { + ID: "dir/file1.md", + Name: "file1.md", + Path: "dir/file1.md", + }, + }, }, - Fixtures: NewMapFS(), - Validate: func(t *testing.T, result any, err error) { - require.NoError(t, err) - files := result.([]storage.FileNode) - require.Len(t, files, 2) - assert.Equal(t, "dir", files[0].Name) - assert.Equal(t, "file1.md", files[1].Name) - assert.Len(t, files[0].Children, 1) - assert.Equal(t, "file2.md", files[0].Children[0].Name) + want: storage.FileNode{ + ID: "dir", + Name: "dir", + Path: "dir", + Children: []storage.FileNode{ + { + ID: "dir/file1.md", + Name: "file1.md", + Path: "dir/file1.md", + }, + }, }, }, } - for _, tc := range tests { - t.Run(tc.Name, func(t *testing.T) { - fs := tc.Fixtures.(*MapFS) - srv := storage.NewServiceWithFS("/test/root", fs) - tc.Setup(t, tc.Fixtures) - files, err := srv.ListFilesRecursively(1, 1) - tc.Validate(t, files, err) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := tc.node // Now we're testing the actual node structure + + if got.ID != tc.want.ID { + t.Errorf("ID = %v, want %v", got.ID, tc.want.ID) + } + if got.Name != tc.want.Name { + t.Errorf("Name = %v, want %v", got.Name, tc.want.Name) + } + if got.Path != tc.want.Path { + t.Errorf("Path = %v, want %v", got.Path, tc.want.Path) + } + if len(got.Children) != len(tc.want.Children) { + t.Errorf("len(Children) = %v, want %v", len(got.Children), len(tc.want.Children)) + } + // Add deep comparison of children if they exist + if len(got.Children) > 0 { + for i := range got.Children { + if got.Children[i].ID != tc.want.Children[i].ID { + t.Errorf("Children[%d].ID = %v, want %v", i, got.Children[i].ID, tc.want.Children[i].ID) + } + if got.Children[i].Name != tc.want.Children[i].Name { + t.Errorf("Children[%d].Name = %v, want %v", i, got.Children[i].Name, tc.want.Children[i].Name) + } + if got.Children[i].Path != tc.want.Children[i].Path { + t.Errorf("Children[%d].Path = %v, want %v", i, got.Children[i].Path, tc.want.Children[i].Path) + } + } + } + }) + } +} + +func TestListFilesRecursively(t *testing.T) { + mockFS := NewMockFS() + s := storage.NewServiceWithFS("test-root", mockFS) + + t.Run("empty directory", func(t *testing.T) { + mockFS.ReadDirReturns = map[string]struct { + entries []fs.DirEntry + err error + }{ + "test-root/1/1": { + entries: []fs.DirEntry{}, + err: nil, + }, + } + + files, err := s.ListFilesRecursively(1, 1) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(files) != 0 { + t.Errorf("expected empty file list, got %v", files) + } + }) + + t.Run("directory with files", func(t *testing.T) { + mockFS.ReadDirReturns = map[string]struct { + entries []fs.DirEntry + err error + }{ + "test-root/1/1": { + entries: []fs.DirEntry{ + NewMockDirEntry("file1.md", false), + NewMockDirEntry("file2.md", false), + }, + err: nil, + }, + } + + files, err := s.ListFilesRecursively(1, 1) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(files) != 2 { + t.Errorf("expected 2 files, got %d", len(files)) + } + }) + + t.Run("nested directories", func(t *testing.T) { + mockFS.ReadDirReturns = map[string]struct { + entries []fs.DirEntry + err error + }{ + "test-root/1/1": { + entries: []fs.DirEntry{ + NewMockDirEntry("dir1", true), + NewMockDirEntry("file1.md", false), + }, + err: nil, + }, + "test-root/1/1/dir1": { + entries: []fs.DirEntry{ + NewMockDirEntry("file2.md", false), + }, + err: nil, + }, + } + + files, err := s.ListFilesRecursively(1, 1) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(files) != 2 { // dir1 and file1.md + t.Errorf("expected 2 entries at root, got %d", len(files)) + } + + // Find directory and check its children + var dirFound bool + for _, f := range files { + if f.Name == "dir1" { + dirFound = true + if len(f.Children) != 1 { + t.Errorf("expected 1 child in dir1, got %d", len(f.Children)) + } + } + } + if !dirFound { + t.Error("directory 'dir1' not found in results") + } + }) +} + +func TestGetFileContent(t *testing.T) { + mockFS := NewMockFS() + s := storage.NewServiceWithFS("test-root", mockFS) + + testCases := []struct { + name string + userID int + workspaceID int + filePath string + mockData []byte + mockErr error + wantErr bool + }{ + { + name: "successful read", + userID: 1, + workspaceID: 1, + filePath: "test.md", + mockData: []byte("test content"), + mockErr: nil, + wantErr: false, + }, + { + name: "file not found", + userID: 1, + workspaceID: 1, + filePath: "nonexistent.md", + mockData: nil, + mockErr: fs.ErrNotExist, + wantErr: true, + }, + { + name: "invalid path", + userID: 1, + workspaceID: 1, + filePath: "../../../etc/passwd", + mockData: nil, + mockErr: nil, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + expectedPath := filepath.Join("test-root", "1", "1", tc.filePath) + mockFS.ReadFileReturns[expectedPath] = struct { + data []byte + err error + }{tc.mockData, tc.mockErr} + + content, err := s.GetFileContent(tc.userID, tc.workspaceID, tc.filePath) + + if tc.wantErr { + if err == nil { + t.Error("expected error, got nil") + } + return + } + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if string(content) != string(tc.mockData) { + t.Errorf("content = %q, want %q", content, tc.mockData) + } + + if mockFS.ReadCalls[expectedPath] != 1 { + t.Errorf("expected 1 read call for %s, got %d", expectedPath, mockFS.ReadCalls[expectedPath]) + } + }) + } +} + +func TestSaveFile(t *testing.T) { + mockFS := NewMockFS() + s := storage.NewServiceWithFS("test-root", mockFS) + + testCases := []struct { + name string + userID int + workspaceID int + filePath string + content []byte + mockErr error + wantErr bool + }{ + { + name: "successful save", + userID: 1, + workspaceID: 1, + filePath: "test.md", + content: []byte("test content"), + mockErr: nil, + wantErr: false, + }, + { + name: "invalid path", + userID: 1, + workspaceID: 1, + filePath: "../../../etc/passwd", + content: []byte("test content"), + mockErr: nil, + wantErr: true, + }, + { + name: "write error", + userID: 1, + workspaceID: 1, + filePath: "test.md", + content: []byte("test content"), + mockErr: fs.ErrPermission, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + mockFS.WriteFileError = tc.mockErr + err := s.SaveFile(tc.userID, tc.workspaceID, tc.filePath, tc.content) + + if tc.wantErr { + if err == nil { + t.Error("expected error, got nil") + } + return + } + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + expectedPath := filepath.Join("test-root", "1", "1", tc.filePath) + if content, ok := mockFS.WriteCalls[expectedPath]; ok { + if string(content) != string(tc.content) { + t.Errorf("written content = %q, want %q", content, tc.content) + } + } else { + t.Error("expected write call not made") + } + }) + } +} + +func TestDeleteFile(t *testing.T) { + mockFS := NewMockFS() + s := storage.NewServiceWithFS("test-root", mockFS) + + testCases := []struct { + name string + userID int + workspaceID int + filePath string + mockErr error + wantErr bool + }{ + { + name: "successful delete", + userID: 1, + workspaceID: 1, + filePath: "test.md", + mockErr: nil, + wantErr: false, + }, + { + name: "invalid path", + userID: 1, + workspaceID: 1, + filePath: "../../../etc/passwd", + mockErr: nil, + wantErr: true, + }, + { + name: "file not found", + userID: 1, + workspaceID: 1, + filePath: "nonexistent.md", + mockErr: fs.ErrNotExist, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + mockFS.RemoveError = tc.mockErr + err := s.DeleteFile(tc.userID, tc.workspaceID, tc.filePath) + + if tc.wantErr { + if err == nil { + t.Error("expected error, got nil") + } + return + } + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + expectedPath := filepath.Join("test-root", "1", "1", tc.filePath) + found := false + for _, p := range mockFS.RemoveCalls { + if p == expectedPath { + found = true + break + } + } + if !found { + t.Error("expected delete call not made") + } }) } }