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
feat: guess subdomains based on conventions (DEV-1979) #445
Conversation
DEV-1979 DSP-TOOLS: guess subdomains based on conventions
Problemhttps://docs.dasch.swiss/2023.03.02/DSP-TOOLS/cli-commands/#before-starting-have-in-mind-the-subdomains-of-a-dsp-server explains how to address the subdomains of a DSP server. But if a user makes the mistake to address a server with admin.dasch.swiss instead of api.dasch.swiss, he doesn't get a useful error message. Even worse: If he forgets to specify the SIPI server for an xmlupload, DSP-TOOLS tries to send the multimedia files to a localhost SIPI, which leads to cryptic error messages. SolutionDon't care about subdomains at all, and just rely on the convention that admin=DSP-APP, api=DSP-API, iiif=SIPI. We could then get rid of this complicated paragraph that makes user's lives difficult: https://docs.dasch.swiss/2023.03.02/DSP-TOOLS/cli-commands/#before-starting-have-in-mind-the-subdomains-of-a-dsp-server. |
DEV-1979 DSP-TOOLS: guess subdomains based on conventions
Problemhttps://docs.dasch.swiss/2023.03.02/DSP-TOOLS/cli-commands/#before-starting-have-in-mind-the-subdomains-of-a-dsp-server explains how to address the subdomains of a DSP server. But if a user makes the mistake to address a server with admin.dasch.swiss instead of api.dasch.swiss, he doesn't get a useful error message. Even worse: If he forgets to specify the SIPI server for an xmlupload, DSP-TOOLS tries to send the multimedia files to a localhost SIPI, which leads to cryptic error messages. SolutionDon't care about subdomains at all, and just rely on the convention that admin=DSP-APP, api=DSP-API, iiif=SIPI. We could then get rid of this complicated paragraph that makes user's lives difficult: https://docs.dasch.swiss/2023.03.02/DSP-TOOLS/cli-commands/#before-starting-have-in-mind-the-subdomains-of-a-dsp-server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some comments, feel free to apply or ignore what you see fit
cls.positive_testcases = { | ||
"https://0.0.0.0:3333/": [ | ||
cls.default_dsp_api_url, | ||
cls.default_sipi_url, | ||
], | ||
"0.0.0.0:3333": [ | ||
cls.default_dsp_api_url, | ||
cls.default_sipi_url, | ||
], | ||
"localhost:3333": [ | ||
cls.default_dsp_api_url, | ||
cls.default_sipi_url, | ||
], | ||
"https://admin.dasch.swiss": [ | ||
"https://api.dasch.swiss", | ||
"https://iiif.dasch.swiss", | ||
], | ||
"https://api.dasch.swiss": [ | ||
"https://api.dasch.swiss", | ||
"https://iiif.dasch.swiss", | ||
], | ||
"https://app.dasch.swiss": [ | ||
"https://api.dasch.swiss", | ||
"https://iiif.dasch.swiss", | ||
], | ||
"https://iiif.dasch.swiss": [ | ||
"https://api.dasch.swiss", | ||
"https://iiif.dasch.swiss", | ||
], | ||
"https://dasch.swiss": [ | ||
"https://api.dasch.swiss", | ||
"https://iiif.dasch.swiss", | ||
], | ||
"dasch.swiss": [ | ||
"https://api.dasch.swiss", | ||
"https://iiif.dasch.swiss", | ||
], | ||
"http://admin.test.dasch.swiss/": [ | ||
"https://api.test.dasch.swiss", | ||
"https://iiif.test.dasch.swiss", | ||
], | ||
"http://app.staging.dasch.swiss/": [ | ||
"https://api.staging.dasch.swiss", | ||
"https://iiif.staging.dasch.swiss", | ||
], | ||
"https://demo.dasch.swiss/": [ | ||
"https://api.demo.dasch.swiss", | ||
"https://iiif.demo.dasch.swiss", | ||
], | ||
"http://api.dev.dasch.swiss/": [ | ||
"https://api.dev.dasch.swiss", | ||
"https://iiif.dev.dasch.swiss", | ||
], | ||
"dev-02.dasch.swiss": [ | ||
"https://api.dev-02.dasch.swiss", | ||
"https://iiif.dev-02.dasch.swiss", | ||
], | ||
"082a-test-server.dasch.swiss": [ | ||
"https://api.082a-test-server.dasch.swiss", | ||
"https://iiif.082a-test-server.dasch.swiss", | ||
], | ||
"admin.08F4-test-server.dasch.swiss": [ | ||
"https://api.08F4-test-server.dasch.swiss", | ||
"https://iiif.08F4-test-server.dasch.swiss", | ||
], | ||
"app.08F4-test-server.dasch.swiss": [ | ||
"https://api.08F4-test-server.dasch.swiss", | ||
"https://iiif.08F4-test-server.dasch.swiss", | ||
], | ||
"iiif.E5bC-test-server.dasch.swiss": [ | ||
"https://api.E5bC-test-server.dasch.swiss", | ||
"https://iiif.E5bC-test-server.dasch.swiss", | ||
], | ||
"not-yet-0826-test-server.dasch.swiss": [ | ||
"https://api.not-yet-0826-test-server.dasch.swiss", | ||
"https://iiif.not-yet-0826-test-server.dasch.swiss", | ||
], | ||
"https://admin.ls-prod-server.dasch.swiss": [ | ||
"https://api.ls-prod-server.dasch.swiss", | ||
"https://iiif.ls-prod-server.dasch.swiss", | ||
], | ||
"https://ls-test-server.dasch.swiss": [ | ||
"https://api.ls-test-server.dasch.swiss", | ||
"https://iiif.ls-test-server.dasch.swiss", | ||
], | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of this kind of test parameterisation: if you want to test arbitrary strings because you're concerned the behaviour might be flaky, then you should use a framework for property-based testing (like Hypothesis); otherwise you should identify the different possible code paths and test those systematically with one test per possible case.
If you expect a different behaviour for "082a-test-server.dasch.swiss"
and "http://api.dev.dasch.swiss/"
then this test either tests the regex you wrote (if you don't trust that) or the correctness of the python regex implementation. If the first is the case, then factor out your regexes and test them separately, if the latter is the case, then don't! 😉
And if you reduce the number of cases you want to test, then you don't need to parametrise your tests, and instead you can have a dedicated test method for each case you want to test, which gives you clearer and more expressive test results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is another case: I'd like my unit tests to be an executable requirements specification. I think I have read that in David Farley's "Modern Software Engineering", but I'm not sure.
The idea that I have in mind: Some day, someone might come across and modify my code, so that by mistake, one of the subdomains isn't covered any more. So I want my unit tests to be a protection against future harm.
Does that make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, but I argue that it's a trade-off: Of course better coverage gives confidence for future code changes. But at the same time, test cases come with a maintenance cost - if requirements change, not only production code but also test code has to be adopted. That's why the granularity and amount of tests are a balance to get right.
(That's an issue we're struggling with, both in the API and the JS-LIB: We have huge amounts of "reference test data" and normally it takes longer to update "fix" all the tests afterwards than to implement an actual feature. I don't think this case is comparable - but I'm a bit hyper-sensitive to that now... 😉 )
If you feel that testing all this helps, then I'm 100% fine with it, but it may not be the kind of testing I currently like to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay - I see your point, and will keep it in mind. thanks for the input, it's always interesting to exchange thoughts with you, and I can learn a lot from your comments :-)
test/unittests/test_cli.py
Outdated
api_url_canonical, sipi_url = expected | ||
args_orig = argparse.Namespace( | ||
action="xmlupload", | ||
server=api_url_orig, | ||
user="some.user@dasch.swiss", | ||
password="password", | ||
xmlfile="data.xml", | ||
) | ||
args_new = _derive_sipi_url( | ||
parsed_arguments=args_orig, | ||
default_dsp_api_url=self.default_dsp_api_url, | ||
default_sipi_url=self.default_sipi_url, | ||
) | ||
self.assertEqual(args_new.server, api_url_canonical) | ||
self.assertEqual(args_new.sipi_url, sipi_url) # pylint: disable=no-member |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see my comment above on the information flow: if _derive_sipi_url()
is changed to a str -> tuple[str, str]
function, then this test can be simplified a lot:
api, sipi = _derive_sipi_url("localhost:3333") # or whatever you want to test
self.assertEqual(api, "http://0.0.0.0:3333")
self.assertEqual(sipi, "http://0.0.0.0:1024")
and as such it's also not too much to have separate test methods:
def test_derive_sipi_localhost(self) -> None:
api, sipi = _derive_sipi_url("localhost:3333")
self.assertEqual(api, "http://0.0.0.0:3333")
self.assertEqual(sipi, "http://0.0.0.0:1024")
def test_derive_sipi_localhost_zeros(self) -> None:
api, sipi = _derive_sipi_url("0.0.0.0:3333")
self.assertEqual(api, "http://0.0.0.0:3333")
self.assertEqual(sipi, "http://0.0.0.0:1024")
etc.
No description provided.