Skip to content

Commit 1a804dc

Browse files
committed
squashed a bug
1 parent 0c333ab commit 1a804dc

5 files changed

Lines changed: 342 additions & 273 deletions

File tree

src/datacustomcode/file/path/default.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def find_file_path(self, file_name: str) -> Path:
7575

7676
file_path = self._resolve_file_path(file_name)
7777

78-
if not file_path:
78+
if not file_path.exists():
7979
raise FileNotFoundError(
8080
f"File '{file_name}' not found in any search location"
8181
)
@@ -126,7 +126,6 @@ def _get_code_package_file_path(self, file_name: str) -> Path:
126126
"""
127127
relative_path = f"{self.code_package}/{self.file_folder}/{file_name}"
128128
return Path(relative_path)
129-
# return Path.cwd().joinpath(relative_path)
130129

131130
def _find_config_file(self) -> Optional[Path]:
132131
"""Find the configuration file in the current directory tree.

tests/file/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright (c) 2025, Salesforce, Inc.
2+
# SPDX-License-Identifier: Apache-2
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.

tests/file/path/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright (c) 2025, Salesforce, Inc.
2+
# SPDX-License-Identifier: Apache-2
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.

tests/file/test_path_default.py

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
# Copyright (c) 2025, Salesforce, Inc.
2+
# SPDX-License-Identifier: Apache-2
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
from __future__ import annotations
16+
17+
import os
18+
import tempfile
19+
from pathlib import Path
20+
from unittest.mock import MagicMock, patch
21+
22+
import pytest
23+
24+
from datacustomcode.file.path.default import (
25+
DefaultFindFilePath,
26+
FileNotFoundError,
27+
FileReaderError,
28+
)
29+
30+
31+
class TestDefaultFindFilePath:
32+
"""Test cases for DefaultFindFilePath class."""
33+
34+
def test_init_with_defaults(self):
35+
"""Test initialization with default values."""
36+
finder = DefaultFindFilePath()
37+
38+
assert finder.code_package == "payload"
39+
assert finder.file_folder == "files"
40+
assert finder.config_file == "config.json"
41+
42+
def test_init_with_custom_values(self):
43+
"""Test initialization with custom values."""
44+
finder = DefaultFindFilePath(
45+
code_package="custom_package",
46+
file_folder="custom_files",
47+
config_file="custom_config.json"
48+
)
49+
50+
assert finder.code_package == "custom_package"
51+
assert finder.file_folder == "custom_files"
52+
assert finder.config_file == "custom_config.json"
53+
54+
def test_find_file_path_empty_filename(self):
55+
"""Test find_file_path with empty filename raises ValueError."""
56+
finder = DefaultFindFilePath()
57+
58+
with pytest.raises(ValueError, match="file_name cannot be empty"):
59+
finder.find_file_path("")
60+
61+
with pytest.raises(ValueError, match="file_name cannot be empty"):
62+
finder.find_file_path(None)
63+
64+
def test_find_file_path_file_not_found(self):
65+
"""Test find_file_path when file doesn't exist raises FileNotFoundError."""
66+
finder = DefaultFindFilePath()
67+
68+
with patch.object(finder, '_resolve_file_path') as mock_resolve:
69+
mock_path = MagicMock()
70+
mock_path.exists.return_value = False
71+
mock_resolve.return_value = mock_path
72+
73+
with pytest.raises(FileNotFoundError, match="File 'test.txt' not found in any search location"):
74+
finder.find_file_path("test.txt")
75+
76+
def test_find_file_path_success(self):
77+
"""Test find_file_path when file exists returns Path."""
78+
finder = DefaultFindFilePath()
79+
80+
with patch.object(finder, '_resolve_file_path') as mock_resolve:
81+
mock_path = MagicMock()
82+
mock_path.exists.return_value = True
83+
mock_resolve.return_value = mock_path
84+
85+
result = finder.find_file_path("test.txt")
86+
87+
assert result == mock_path
88+
mock_resolve.assert_called_once_with("test.txt")
89+
90+
def test_resolve_file_path_code_package_exists(self):
91+
"""Test _resolve_file_path when code package exists and file is found."""
92+
finder = DefaultFindFilePath()
93+
94+
with patch.object(finder, '_code_package_exists', return_value=True) as mock_exists:
95+
with patch.object(finder, '_get_code_package_file_path') as mock_get_path:
96+
mock_path = MagicMock()
97+
mock_path.exists.return_value = True
98+
mock_get_path.return_value = mock_path
99+
100+
result = finder._resolve_file_path("test.txt")
101+
102+
assert result == mock_path
103+
mock_exists.assert_called_once()
104+
mock_get_path.assert_called_once_with("test.txt")
105+
106+
def test_resolve_file_path_code_package_exists_file_not_found(self):
107+
"""Test _resolve_file_path when code package exists but file not found, falls back to config."""
108+
finder = DefaultFindFilePath()
109+
110+
with patch.object(finder, '_code_package_exists', return_value=True) as mock_exists:
111+
with patch.object(finder, '_get_code_package_file_path') as mock_get_path:
112+
with patch.object(finder, '_find_config_file') as mock_find_config:
113+
with patch.object(finder, '_get_config_based_file_path') as mock_get_config_path:
114+
# Code package file doesn't exist
115+
mock_code_path = MagicMock()
116+
mock_code_path.exists.return_value = False
117+
mock_get_path.return_value = mock_code_path
118+
119+
# Config file exists and config-based file exists
120+
mock_config_path = MagicMock()
121+
mock_find_config.return_value = mock_config_path
122+
123+
mock_config_file_path = MagicMock()
124+
mock_config_file_path.exists.return_value = True
125+
mock_get_config_path.return_value = mock_config_file_path
126+
127+
result = finder._resolve_file_path("test.txt")
128+
129+
assert result == mock_config_file_path
130+
mock_find_config.assert_called_once()
131+
mock_get_config_path.assert_called_once_with("test.txt", mock_config_path)
132+
133+
def test_resolve_file_path_fallback_to_filename(self):
134+
"""Test _resolve_file_path falls back to Path(filename) when no other location works."""
135+
finder = DefaultFindFilePath()
136+
137+
with patch.object(finder, '_code_package_exists', return_value=False):
138+
with patch.object(finder, '_find_config_file', return_value=None):
139+
result = finder._resolve_file_path("test.txt")
140+
141+
assert result == Path("test.txt")
142+
143+
def test_code_package_exists_true(self):
144+
"""Test _code_package_exists returns True when directory exists."""
145+
finder = DefaultFindFilePath()
146+
147+
with patch('os.path.exists', return_value=True):
148+
assert finder._code_package_exists() is True
149+
150+
def test_code_package_exists_false(self):
151+
"""Test _code_package_exists returns False when directory doesn't exist."""
152+
finder = DefaultFindFilePath()
153+
154+
with patch('os.path.exists', return_value=False):
155+
assert finder._code_package_exists() is False
156+
157+
def test_get_code_package_file_path(self):
158+
"""Test _get_code_package_file_path constructs correct path."""
159+
finder = DefaultFindFilePath()
160+
161+
result = finder._get_code_package_file_path("test.txt")
162+
163+
expected = Path("payload/files/test.txt")
164+
assert result == expected
165+
166+
def test_get_code_package_file_path_custom_values(self):
167+
"""Test _get_code_package_file_path with custom values."""
168+
finder = DefaultFindFilePath(
169+
code_package="custom_package",
170+
file_folder="custom_files"
171+
)
172+
173+
result = finder._get_code_package_file_path("test.txt")
174+
175+
expected = Path("custom_package/custom_files/test.txt")
176+
assert result == expected
177+
178+
def test_find_config_file_found(self):
179+
"""Test _find_config_file when config file is found."""
180+
finder = DefaultFindFilePath()
181+
182+
with patch.object(finder, '_find_file_in_tree') as mock_find:
183+
mock_path = MagicMock()
184+
mock_find.return_value = mock_path
185+
186+
result = finder._find_config_file()
187+
188+
assert result == mock_path
189+
mock_find.assert_called_once_with("config.json", Path.cwd())
190+
191+
def test_find_config_file_not_found(self):
192+
"""Test _find_config_file when config file is not found."""
193+
finder = DefaultFindFilePath()
194+
195+
with patch.object(finder, '_find_file_in_tree', return_value=None):
196+
result = finder._find_config_file()
197+
198+
assert result is None
199+
200+
def test_get_config_based_file_path(self):
201+
"""Test _get_config_based_file_path constructs correct path."""
202+
finder = DefaultFindFilePath()
203+
config_path = Path("/some/path/config.json")
204+
205+
result = finder._get_config_based_file_path("test.txt", config_path)
206+
207+
expected = Path("files/test.txt")
208+
assert result == expected
209+
210+
def test_get_config_based_file_path_custom_folder(self):
211+
"""Test _get_config_based_file_path with custom file folder."""
212+
finder = DefaultFindFilePath(file_folder="custom_files")
213+
config_path = Path("/some/path/config.json")
214+
215+
result = finder._get_config_based_file_path("test.txt", config_path)
216+
217+
expected = Path("custom_files/test.txt")
218+
assert result == expected
219+
220+
def test_find_file_in_tree_found(self):
221+
"""Test _find_file_in_tree when file is found."""
222+
finder = DefaultFindFilePath()
223+
224+
with tempfile.TemporaryDirectory() as temp_dir:
225+
temp_path = Path(temp_dir)
226+
test_file = temp_path / "test.txt"
227+
test_file.write_text("test content")
228+
229+
result = finder._find_file_in_tree("test.txt", temp_path)
230+
231+
assert result is not None
232+
assert result.name == "test.txt"
233+
234+
def test_find_file_in_tree_not_found(self):
235+
"""Test _find_file_in_tree when file is not found."""
236+
finder = DefaultFindFilePath()
237+
238+
with tempfile.TemporaryDirectory() as temp_dir:
239+
temp_path = Path(temp_dir)
240+
241+
result = finder._find_file_in_tree("nonexistent.txt", temp_path)
242+
243+
assert result is None
244+
245+
def test_find_file_in_tree_multiple_matches(self):
246+
"""Test _find_file_in_tree when multiple files match, returns first one."""
247+
finder = DefaultFindFilePath()
248+
249+
with tempfile.TemporaryDirectory() as temp_dir:
250+
temp_path = Path(temp_dir)
251+
252+
# Create multiple files with same name in different subdirectories
253+
(temp_path / "subdir1").mkdir()
254+
(temp_path / "subdir2").mkdir()
255+
256+
file1 = temp_path / "subdir1" / "test.txt"
257+
file2 = temp_path / "subdir2" / "test.txt"
258+
259+
file1.write_text("content1")
260+
file2.write_text("content2")
261+
262+
result = finder._find_file_in_tree("test.txt", temp_path)
263+
264+
assert result is not None
265+
assert result.name == "test.txt"
266+
# Should return one of the files (implementation returns first found)
267+
268+
def test_integration_find_file_path_success(self):
269+
"""Test integration: find_file_path with real file system."""
270+
finder = DefaultFindFilePath()
271+
272+
with tempfile.TemporaryDirectory() as temp_dir:
273+
# Create a test file
274+
test_file = Path(temp_dir) / "test.txt"
275+
test_file.write_text("test content")
276+
277+
# Mock the code package to point to our temp directory
278+
finder.code_package = temp_dir
279+
finder.file_folder = ""
280+
281+
result = finder.find_file_path("test.txt")
282+
283+
assert result == test_file
284+
assert result.exists()
285+
286+
def test_integration_find_file_path_not_found(self):
287+
"""Test integration: find_file_path when file doesn't exist."""
288+
finder = DefaultFindFilePath()
289+
290+
with tempfile.TemporaryDirectory() as temp_dir:
291+
# Don't create any files
292+
finder.code_package = temp_dir
293+
finder.file_folder = ""
294+
295+
with pytest.raises(FileNotFoundError):
296+
finder.find_file_path("nonexistent.txt")
297+
298+
299+
class TestFileReaderError:
300+
"""Test cases for FileReaderError exception classes."""
301+
302+
def test_file_reader_error_inheritance(self):
303+
"""Test FileReaderError inherits from Exception."""
304+
error = FileReaderError("test message")
305+
assert isinstance(error, Exception)
306+
assert str(error) == "test message"
307+
308+
def test_file_not_found_error_inheritance(self):
309+
"""Test FileNotFoundError inherits from FileReaderError."""
310+
error = FileNotFoundError("file not found")
311+
assert isinstance(error, FileReaderError)
312+
assert isinstance(error, Exception)
313+
assert str(error) == "file not found"

0 commit comments

Comments
 (0)