Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-37917: Add testing against real webDAV server for HttpResourcePath #40

Merged
merged 22 commits into from Feb 15, 2023

Conversation

airnandez
Copy link
Contributor

Checklist

  • ran Jenkins
  • added a release note for user-visible changes to doc/changes

Copy link
Member

@timj timj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@airnandez thanks for adding these tests. I took the liberty of trying to enable the full ResourcePath test suite now that we have a functioning WebDAV server and not a mocked one. It didn't take many lines to enable it and it did find two compatibility issues.

  1. When a directory is not created because it's not a directory we have to raise NotADirectoryError (I fixed this).
  2. When attempting to uri.write() when the parent directory does not exist, the other backends make the directory automatically. This does not happen in webdav and so the 4 tests fail with error 409.

Now that we have a WebDAV server involved in the tests I wonder if the mocked test case is worth keeping now. Its main reason to exist is that it tries to test code when there is no webdav server running so maybe that's enough of a reason to keep it but it's now a lot of code to continue to support and it's also not that easy to follow.

* Run generic Resource tests in addition to HTTP-specific tests
* Skip tests again plain HTTP servers because they cannot succeed since
  methods such as mkdir(), exists() for a directory or remove() for
  a directory are not supported by those servers.
* Allow to run the same set of tests  against real servers in the
  development environment by allowing to specify the server URL
  via an environment variable.
* Skip mock-based tests for now that we can run against a real
  local server launched at test time.
@airnandez
Copy link
Contributor Author

... uri.write() when the parent directory does not exist, the other backends make the directory automatically.

This is fixed now.

Now that we have a WebDAV server involved in the tests I wonder if the mocked test case is worth keeping now.

I agree that mocking responses is tedious and not very reliable. For the time being, I skip that test as well as all the generic tests that need HttpResourcePath.walk() to be implemented, which is not yet the case.

Also, let me draw your attention to the fact that it is not possible to run the generic tests against a plain HTTP backend, since several methods like mkdir(), remove() for directories, exists() for directories cannot be implemented.

Finally, I ran the same set of generic tests and the webDAV-specific tests against 4 different live DAV servers in my development environment.

Copy link
Member

@timj timj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Some minor cleanups suggested (especially removing code completely that we are always skipping).

# walk() and we can fully test that GenericTestCase passes against a
# real webDAV server. For the time being we skip it because mocking
# all the possible responses for every possible situation is tedious.
@unittest.skipIf(True, "Skipping test with mocked responses.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather we deleted the test case completely if we have no intention of keeping it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Tests with mocked responses removed.

# Execution of GenericTestCase against a plain HTTP server cannot succeed:
# such a server does not support mkdir(), exists() for a directory, remove()
# for a directory.
@unittest.skipIf(True, "Skipping testing with plain HTTP server.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we skipping this? The GenericTestCase code works fine and is there to ensure that the path operations function properly for the HttpResourcePath -- it's specifically designed not to do any I/O and all tests pass when enabled. Please remove the skip and comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong understanding on my side. I reactivated this test.

# developer environment by initializing the environment variable
# with the URL of the server, e.g.
# https://dav.example.org:1234/path/to/top/dir
u = urllib.parse.urlparse(test_endpoint)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is in the ResourcePath codebase can we use it rather than the explicit parsing?

test_uri = ResourcePath(test_endpoint, forceDirectory=True)
cls.scheme = test_uri.scheme
cls.netloc = test_uri.netloc
cls.base_path = test_uri.path

?

Copy link
Contributor Author

@airnandez airnandez Feb 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention was to get a clean path, to avoid potential issues with super-sensitive webDAV servers:

>>> p = ResourcePath("https://host.example.org:12345////not/very////clean/path///")
>>> p.path
'////not/very////clean/path///'

By a "clean path" I mean a path in which each component is separated by a single /, e.g.:

/not/very/clean/path

A path with several separators is syntactically correct, though.

I reverted to use ResourcePath for parsing.

u = urllib.parse.urlparse(test_endpoint)
cls.scheme = u.scheme
cls.netloc = u.netloc
cls.base_path = "/" + u.path.strip("/")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we stripping the leading "/" and then adding it back again? Why are we stripping the trailing slash when the code that uses it takes that into account?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above.


path = path.lstrip("/")
if self.base_path is not None:
path = self.base_path.strip("/") + "/" + path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably clearer to do:

path = os.path.join(self.base_path, path)

since that will take care of the trailing slash automatically.


path = path.lstrip("/")
if self.base_path is not None:
path = self.base_path.strip("/") + "/" + path
if path.startswith("/"):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path.lstrip at the top makes this line a no-op I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above.

@@ -428,7 +436,10 @@ def setUp(self) -> None:
self.tmpdir = ResourcePath(makeTestTempDir(self.testdir))
else:
# Create tmp directory relative to the test root.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change to "Create random tmp directory"

shutil.rmtree(cls.webdav_tmpdir, ignore_errors=True)

def setUp(self):
super().setUp()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a method that just calls super() can be deleted entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@codecov
Copy link

codecov bot commented Feb 13, 2023

Codecov Report

Base: 84.74% // Head: 84.14% // Decreases project coverage by -0.60% ⚠️

Coverage data is based on head (4087c3e) compared to base (14c3e05).
Patch coverage: 87.76% of modified lines in pull request are covered.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #40      +/-   ##
==========================================
- Coverage   84.74%   84.14%   -0.60%     
==========================================
  Files          27       27              
  Lines        3415     3476      +61     
  Branches      713      714       +1     
==========================================
+ Hits         2894     2925      +31     
- Misses        408      435      +27     
- Partials      113      116       +3     
Impacted Files Coverage Δ
python/lsst/resources/tests.py 97.57% <66.66%> (-0.39%) ⬇️
python/lsst/resources/http.py 77.95% <75.00%> (-2.43%) ⬇️
tests/test_http.py 92.51% <89.91%> (-5.23%) ⬇️
tests/test_s3.py 87.34% <100.00%> (ø)
python/lsst/resources/_resourcePath.py 94.58% <0.00%> (+1.23%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

We are using an external webDAV server to test against. In addition
we have the possibility now to test against other real webDAV servers
in the develpment setting by specifying via the environment variable
LSST_RESOURCES_HTTP_TEST_SERVER_URL the URL of a server to test
against.

However, since we are no longer mocking responses, in the current
situation we cannot fully test cases where the server does not
support some features (e.g. a plain HTTP server, or a webDAV server
that does not support download a specified range of bytes).
@airnandez airnandez merged commit 3fd76a0 into main Feb 15, 2023
@airnandez airnandez deleted the tickets/DM-37917 branch February 15, 2023 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants