Skip to content

Commit

Permalink
Update for TestTokenQueries.py and TestWebserver.py (#975)
Browse files Browse the repository at this point in the history
* Added dynamic conda environment name to assertion log messages

Using str.format() to update the log message to be matched with the current conda environment name.
Then using encode() to convert it back to a byte string.

------

Python byte strings
https://stackoverflow.com/questions/6224052/what-is-the-difference-between-a-string-and-a-byte-string
https://www.digitalocean.com/community/tutorials/python-string-encode-decode
https://stackoverflow.com/questions/67680296/syntaxerror-f-string-expression-part-cannot-include-a-backslash

Found encode() / decode() usage in bottle.py
e-mission-server/emission/net/api/bottle.py

utf-8 not needed as it is default.

* Changed TestWebserver.py to work with both config files and ENV variables

-------

A. TestWebserver.py
Modified the code to have both approaches to read from config file as well as environment variables.
The config files approach is based on the previous code logic that was used in this file to copy webserver.conf.sample as webserver.conf.
b20cc36

The difference is I'm taking a backup of the webserver.conf file if present. Then writing the test values to this file before running tests. In teardown(), the original webserver.conf is restored from the backup file.

-----------

B. cfc_webapp.py

WEBSERVER_NOT_FOUND_REDIRECT was missing in the config var_map passed to get_config.
It was being read using config.get in the variable not_found_redirect. Hence added it.

* Logging CONDA_DEFAULT_ENV to investigate failed tests

Tests failed in CI/CD.
On testing locally it was able to fetch the `emissiontest` name.
But in the CI/CD, it is defaulting to use the `emission` environment name.

I did test locally using the steps in the manual install workflow file to use the activate_tests.sh and setup_tests.sh scripts.

Now testing locally by running the docker-compose.tests.yml which is used in the test-with-docker workflow.

* Corrected 1st log message for local emission environment

The tests failed since despite using ENV vars, it wasn’t picking up value.
This was because ENV vars are being deleted in setUp().
Added CONDA_DEFAULT_ENV to the skipped env vars.

However, this approach of fetching ENV vars programmatically in the assert statement isn’t correct.
This is because the code in get_config() itself uses ENV vars to set the ret_val which is the exact value as being fetched in setUp(). Thus the values will always match.

———

Then I thought of adding another log message for the `emission` environment name.
While testing this I found that the 1st message isn’t being used in the normal situations when testing locally.
This message is observed when the conf/storage/db.conf file is used instead of the sample file.
But in normal operation, the server repo doesn’t have any conf files to begin with.

Additionally, we now set the DB_HOST as db and WEB_SERVER_HOST as 0.0.0.0 in the Dockerfile by default.

Hence the assert log message should also contain `Config file not  found…` message like the other two messages.
It should also have DB_HOST set to Db like the 3rd message.

* Correcting import for restoreOriginalEnvVars

* Added fourth message for running tests in local docker container

Details in this PR comment:
#975 (comment)

* Revert "Changed TestWebserver.py to work with both config files and ENV variables"

This reverts commit 1dc4275.

* Added WEBSERVER_NOT_FOUND_REDIRECT to config map

WEBSERVER_NOT_FOUND_REDIRECT was missing in the config var_map passed to get_config.
It was being read using config.get in the variable not_found_redirect.

* Simplified tests with two log messages for four test environments

The PATH values vary based on a user's local system and could have additional values which could fail the test in a local environment run without docker containers.

Why do we even need the PATH, PYTHONPATH values in the assertion log messages.
This is because we are printing the config in get_database.py which contains environment variables or config file values.

Now, the question is why to print the entire set of values if we are only using DB_HOST, DB_RESULT_LIMIT?

I am now printing only these values in the get_database.py. This has two benefits:
- Allows to eliminate variable environment variables like PATH that might vary in local testing environments for every user.
- Since PATH is no longer printed, helps avoid issue of 'apple security' values.
- Helps reduce the number of log messages the 4 testing environments: local without docker, local with docker, CI/CD without docker, CI/CD with docker. The two docker environments have the same log message; similarly for the two non-docker environments. Thus we only need two log messages in the assertion tests.

* Logging retrieved db config values before using default values

With reference to this commit:
#975 (comment)

So we have two key things to solve:
- Unnecessary variables like PATH, that may vary for each user to be removed (helps avoid "apple security" values).
- Need to see ENV vars values before being modified by default values in config.get() lines.

Two approaches:
1. Delete PATH, PYTHONPATH from the retrieved config.
- This wouldn't help much since we still need to handle the scenario when db config environment values haven't been set.

2. Using another config dictionary before modifying with default values
- If db config related keys exist, set it to the value from the retrieved config.
- Else if key does not exist in retrieved config, which means config file not present AND environment variables not set, then set it to None.
Note:
- This is not the same as changing the retrieved config value as the original dictionary is still intact.
- Additionally, in the case where config file not present AND environment variables not set, there are no values to before modifying with default values. So manually setting to None should be okay.

* ✅ Unify the expected messages through redaction

Principled fix for the issue outlined in
#975

The fundamental issue is that we check the output to validate that the
script is behaving as expected. However, the script output can vary in
different environments.

We had originally handled this by having multiple expected output strings. But
as we expand the scope of what we print out, the set of expected output strings expanded. Some of the strings also included paths, so had the username embedded, so would be hard to enumerate in the test.

Those logs are also not critical to this test, which is focused on the response
to various arguments passed in to the script.

So we fix this by simply removing the parts that vary across environments, replacing them by `REDACTED`.

As a bonus, this also allows us to collapse the previous two strings into one,
and supports all three testing environments: (i) locally on laptop, (ii)
locally in docker, and (iii) docker CI/CD (although (ii) is not really
supported since it is not tested extensively).

Testing done:

```
$ ./e-mission-py.bash emission/tests/storageTests/TestTokenQueries.py
----------------------------------------------------------------------
Ran 21 tests in 22.862s

OK
```

* Added missing import for 'os' module

Was causing an exception leading to "push not configured" message everytime irrespective of whether config files present or env variables configured.

Code with error:

```
try:
logging.info(f"Push configured for app {push_config.get('PUSH_SERVER_AUTH_TOKEN')} using platform {os.getenv('PUSH_PROVIDER')} with token {os.getenv('PUSH_SERVER_AUTH_TOKEN')[:10]}... of length {len(os.getenv('PUSH_SERVER_AUTH_TOKEN'))}")
except:
logging.warning("push service not configured, push notifications not supported")
```

---------

Co-authored-by: Mahadik, Mukul Chandrakant <[email protected]>
Co-authored-by: Shankari <[email protected]>
  • Loading branch information
3 people authored Sep 10, 2024
1 parent 7f450a5 commit 9fe1b3e
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 19 deletions.
8 changes: 7 additions & 1 deletion emission/core/get_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
config = ecbc.get_config('conf/storage/db.conf',
{"DB_HOST": "timeseries.url", "DB_RESULT_LIMIT": "timeseries.result_limit"})

print("Retrieved config %s" % config)
db_config = {}
for key in ["DB_HOST", "DB_RESULT_LIMIT"]:
if key in config:
db_config[key] = config.get(key)
else:
db_config[key] = None
print("Retrieved config: %s" % db_config)
url = config.get("DB_HOST", "localhost")
result_limit = config.get("DB_RESULT_LIMIT", 250000)

Expand Down
4 changes: 3 additions & 1 deletion emission/integrationTests/storageTests/TestMongodbAuth.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import importlib
import os

import emission.tests.common as etc

class TestMongodbAuth(unittest.TestCase):
def setUp(self):
self.admin_default = pymongo.MongoClient('localhost', username="admin",
Expand All @@ -53,7 +55,7 @@ def setUp(self):
def tearDown(self):
self.admin_auth.command({"dropAllUsersFromDatabase": 1})
logging.debug("Deleting test db environment variables")
ecc.restoreOriginalEnvVars(self.originalDBEnvVars, self.modifiedEnvVars)
etc.restoreOriginalEnvVars(self.originalDBEnvVars, self.modifiedEnvVars)
logging.debug("Finished restoring original db environment variables")
logging.debug("Restored original values are = %s" % self.originalDBEnvVars)
try:
Expand Down
3 changes: 2 additions & 1 deletion emission/net/api/cfc_webapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@

config = ecbc.get_config('conf/net/api/webserver.conf',
{"WEBSERVER_PORT": "server.port", "WEBSERVER_TIMEOUT": "server.timeout",
"WEBSERVER_AUTH": "server.auth", "WEBSERVER_AGGREGATE_CALL_AUTH": "server.aggregate_call_auth"})
"WEBSERVER_AUTH": "server.auth", "WEBSERVER_AGGREGATE_CALL_AUTH": "server.aggregate_call_auth",
"WEBSERVER_NOT_FOUND_REDIRECT": "paths.404_redirect"})
server_port = config.get("WEBSERVER_PORT", 8080)
socket_timeout = config.get("WEBSERVER_TIMEOUT", 3600)
auth_method = config.get("WEBSERVER_AUTH", "skip")
Expand Down
1 change: 1 addition & 0 deletions emission/net/ext_service/push/notify_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import json
import logging
import importlib
import os

import emission.core.backwards_compat_config as ecbc

Expand Down
33 changes: 17 additions & 16 deletions emission/tests/storageTests/TestTokenQueries.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import uuid
import json
import os
import re

#changed all script runs from os() to subprocess.run() for consistency
#TODO clean up commented out os() lines
Expand Down Expand Up @@ -168,25 +169,25 @@ def test_run_script_show(self):
esdt.insert({'token':'y'})
esdt.insert({'token':'z'})
sp = subprocess.run(["python3", "bin/auth/insert_tokens.py", "--show"], capture_output=True)
# The first message is displayed when we run tests locally
# The second is displayed when we run in the CI/CD, but with the local install
# The third is displayed when we run in the docker CI since the `DB_HOST` is set to `db`
self.assertIn(sp.stdout,
[b'Retrieved config {\'DB_HOST\': \'localhost\', \'DB_RESULT_LIMIT\': 250000}\nURL not formatted, defaulting to "Stage_database"\nConnecting to database URL localhost\nx\ny\nz\n',
b'Config file not found, returning a copy of the environment variables instead...\nRetrieved config {\'PYTHONPATH\': \'.\', \'PATH\': \'/home/runner/miniconda-23.5.2/envs/emissiontest/bin:/home/runner/miniconda-23.5.2/condabin:/snap/bin:/home/runner/.local/bin:/opt/pipx_bin:/home/runner/.cargo/bin:/home/runner/.config/composer/vendor/bin:/usr/local/.ghcup/bin:/home/runner/.dotnet/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/runner/.dotnet/tools\', \'LC_CTYPE\': \'C.UTF-8\'}\nURL not formatted, defaulting to "Stage_database"\nConnecting to database URL localhost\nx\ny\nz\n',
b'Config file not found, returning a copy of the environment variables instead...\nRetrieved config {\'PYTHONPATH\': \'.\', \'DB_HOST\': \'db\', \'PATH\': \'/root/miniconda-23.5.2/envs/emissiontest/bin:/root/miniconda-23.5.2/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\', \'LC_CTYPE\': \'C.UTF-8\'}\nURL not formatted, defaulting to "Stage_database"\nConnecting to database URL db\nx\ny\nz\n'
])
# The first message is displayed when we run tests locally or run in the CI/CD, but with the local install
# The second is displayed when we run tests (the `DB_HOST` is set to `db` by default):
# a) in the docker CI or,
# b) locally in a docker container (ad-hoc testing environment; do not expect this to be used)

stripped_out_stdout = re.sub(rb'Retrieved config.*\nURL not formatted.*\nConnecting to database.*', b'REDACTED', sp.stdout)
self.assertEqual(stripped_out_stdout,
b'Config file not found, returning a copy of the environment variables instead...\nREDACTED\nx\ny\nz\n')

def test_run_script_empty(self):
sp = subprocess.run(["python3", "bin/auth/insert_tokens.py"], capture_output=True)
# The first message is displayed when we run tests locally
# The second is displayed when we run in the CI/CD, but with the local install
# The third is displayed when we run in the docker CI since the `DB_HOST` is set to `db`
self.assertIn(sp.stdout,
[b'Retrieved config {\'DB_HOST\': \'localhost\', \'DB_RESULT_LIMIT\': 250000}\nURL not formatted, defaulting to "Stage_database"\nConnecting to database URL localhost\nPlease provide the script with an argument. Use the "--help" option for more details\n',
b'Config file not found, returning a copy of the environment variables instead...\nRetrieved config {\'PYTHONPATH\': \'.\', \'PATH\': \'/home/runner/miniconda-23.5.2/envs/emissiontest/bin:/home/runner/miniconda-23.5.2/condabin:/snap/bin:/home/runner/.local/bin:/opt/pipx_bin:/home/runner/.cargo/bin:/home/runner/.config/composer/vendor/bin:/usr/local/.ghcup/bin:/home/runner/.dotnet/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/runner/.dotnet/tools\', \'LC_CTYPE\': \'C.UTF-8\'}\nURL not formatted, defaulting to "Stage_database"\nConnecting to database URL localhost\nPlease provide the script with an argument. Use the "--help" option for more details\n',
b'Config file not found, returning a copy of the environment variables instead...\nRetrieved config {\'PYTHONPATH\': \'.\', \'DB_HOST\': \'db\', \'PATH\': \'/root/miniconda-23.5.2/envs/emissiontest/bin:/root/miniconda-23.5.2/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\', \'LC_CTYPE\': \'C.UTF-8\'}\nURL not formatted, defaulting to "Stage_database"\nConnecting to database URL db\nPlease provide the script with an argument. Use the "--help" option for more details\n'
])
# The first message is displayed when we run tests locally or run in the CI/CD, but with the local install
# The second is displayed when we run tests (the `DB_HOST` is set to `db` by default):
# a) in the docker CI or,
# b) locally in a docker container (ad-hoc testing environment; do not expect this to be used)
stripped_out_stdout = re.sub(rb'Retrieved config.*\nURL not formatted.*\nConnecting to database.*', b'REDACTED', sp.stdout)
self.assertEqual(stripped_out_stdout,
b'Config file not found, returning a copy of the environment variables instead...\nREDACTED\nPlease provide the script with an argument. Use the "--help" option for more details\n'
)

#test that no two options can be used together
def test_run_script_mutex(self):
Expand Down

0 comments on commit 9fe1b3e

Please sign in to comment.