-
Notifications
You must be signed in to change notification settings - Fork 0
enforce type checking for put_object, encode to utf-8 to match boto3 #2
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,8 +4,10 @@ | |
| from datetime import datetime | ||
|
|
||
| import boto3 | ||
| import pytest | ||
|
|
||
| from s3_encryption import S3EncryptionClient, S3EncryptionClientConfig | ||
| from s3_encryption.exceptions import S3EncryptionClientError | ||
| from s3_encryption.materials.kms_keyring import KmsKeyring | ||
|
|
||
| bucket = os.environ.get("CI_S3_BUCKET", "s3ec-python-github-test-bucket") | ||
|
|
@@ -15,7 +17,7 @@ | |
| ) | ||
|
|
||
|
|
||
| def test_simple_roundtrip(): | ||
| def test_simple_roundtrip_ascii_string(): | ||
| key = "simple-rt" | ||
| key += datetime.now().strftime("%Y-%m-%d-%H:%M:%S") | ||
|
|
||
|
|
@@ -101,3 +103,187 @@ def test_no_body_roundtrip(): | |
| print( | ||
| "Success! Object with no Body parameter encrypted and decrypted correctly as empty bytes." | ||
| ) | ||
|
|
||
|
|
||
| def test_unicode_string_roundtrip(): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A note that might help you as you progress in writing tests: Here's how you could set that up: |
||
| key = "unicode-string-rt" | ||
| key += datetime.now().strftime("%Y-%m-%d-%H:%M:%S") | ||
|
|
||
| # String with unusual Unicode characters | ||
| data = "Unicode test: 你好, こんにちは, 안녕하세요, Привет, مرحبا, ¡Hola!, ½⅓¼⅕⅙⅐⅛⅑⅒⅔⅖⅗⅘⅙⅚⅜⅝⅞" | ||
|
|
||
| kms_client = boto3.client("kms", region_name=region) | ||
|
|
||
| keyring = KmsKeyring(kms_client, kms_key_id) | ||
|
|
||
| wrapped_client = boto3.client("s3") | ||
| config = S3EncryptionClientConfig(keyring) | ||
| s3ec = S3EncryptionClient(wrapped_client, config) | ||
| s3ec.put_object(Bucket=bucket, Key=key, Body=data) | ||
| get_req = {"Bucket": bucket, "Key": key} | ||
| response = s3ec.get_object(**get_req) | ||
|
|
||
| # Boto3 encodes to utf-8 in put_object but does not | ||
| # decode in get_object; do so manually to complete the | ||
| # round trip | ||
| output = response["Body"].read().decode("utf-8") | ||
| if output != data: | ||
| print("Uh oh! Input and output don't match!") | ||
| print("Input:") | ||
| print(repr(data)) | ||
| print("Output:") | ||
| print(repr(output)) | ||
| raise RuntimeError | ||
| print("Success! Unicode string encrypted and decrypted correctly.") | ||
|
|
||
|
|
||
| def test_specific_encoding_utf8_roundtrip(): | ||
| key = "utf8-encoding-rt" | ||
| key += datetime.now().strftime("%Y-%m-%d-%H:%M:%S") | ||
|
|
||
| # String with mixed characters | ||
| data = "UTF-8 encoding test: 你好, こんにちは, 안녕하세요, Привет, مرحبا, ¡Hola!" | ||
|
|
||
| # Explicitly encode as UTF-8 before sending | ||
| encoded_data = data.encode("utf-8") | ||
|
|
||
| kms_client = boto3.client("kms", region_name=region) | ||
|
|
||
| keyring = KmsKeyring(kms_client, kms_key_id) | ||
|
|
||
| wrapped_client = boto3.client("s3") | ||
| config = S3EncryptionClientConfig(keyring) | ||
| s3ec = S3EncryptionClient(wrapped_client, config) | ||
|
|
||
| # Pass the pre-encoded bytes to put_object | ||
| s3ec.put_object(Bucket=bucket, Key=key, Body=encoded_data) | ||
|
|
||
| get_req = {"Bucket": bucket, "Key": key} | ||
| response = s3ec.get_object(**get_req) | ||
|
|
||
| # Read raw bytes and decode with the same encoding | ||
| output = response["Body"].read().decode("utf-8") | ||
|
|
||
| if output != data: | ||
| print("Uh oh! Input and output don't match!") | ||
| print("Input:") | ||
| print(repr(data)) | ||
| print("Output:") | ||
| print(repr(output)) | ||
| raise RuntimeError | ||
| print("Success! UTF-8 encoded string encrypted and decrypted correctly.") | ||
|
|
||
|
|
||
| def test_specific_encoding_latin1_roundtrip(): | ||
| key = "latin1-encoding-rt" | ||
| key += datetime.now().strftime("%Y-%m-%d-%H:%M:%S") | ||
|
|
||
| # String with Latin-1 compatible characters | ||
| data = "Latin-1 encoding test: éèêë àâäãåá çñ ¿¡ øæå ØÆÅÉÈÊËÀÂÄÃÅÁ" | ||
|
|
||
| # Explicitly encode as Latin-1 before sending | ||
| encoded_data = data.encode("latin-1") | ||
|
|
||
| kms_client = boto3.client("kms", region_name=region) | ||
|
|
||
| keyring = KmsKeyring(kms_client, kms_key_id) | ||
|
|
||
| wrapped_client = boto3.client("s3") | ||
| config = S3EncryptionClientConfig(keyring) | ||
| s3ec = S3EncryptionClient(wrapped_client, config) | ||
|
|
||
| # Pass the pre-encoded bytes to put_object | ||
| s3ec.put_object(Bucket=bucket, Key=key, Body=encoded_data) | ||
|
|
||
| get_req = {"Bucket": bucket, "Key": key} | ||
| response = s3ec.get_object(**get_req) | ||
|
|
||
| # Read raw bytes and decode with the same encoding | ||
| output = response["Body"].read().decode("latin-1") | ||
|
|
||
| if output != data: | ||
| print("Uh oh! Input and output don't match!") | ||
| print("Input:") | ||
| print(repr(data)) | ||
| print("Output:") | ||
| print(repr(output)) | ||
| raise RuntimeError | ||
| print("Success! Latin-1 encoded string encrypted and decrypted correctly.") | ||
|
|
||
|
|
||
| def test_binary_data_roundtrip(): | ||
| key = "binary-data-rt" | ||
| key += datetime.now().strftime("%Y-%m-%d-%H:%M:%S") | ||
|
|
||
| # Create some binary data (not valid in any particular encoding) | ||
| data = bytes([i for i in range(256)]) | ||
|
|
||
| kms_client = boto3.client("kms", region_name=region) | ||
|
|
||
| keyring = KmsKeyring(kms_client, kms_key_id) | ||
|
|
||
| wrapped_client = boto3.client("s3") | ||
| config = S3EncryptionClientConfig(keyring) | ||
| s3ec = S3EncryptionClient(wrapped_client, config) | ||
|
|
||
| # Pass the binary data directly | ||
| s3ec.put_object(Bucket=bucket, Key=key, Body=data) | ||
|
|
||
| get_req = {"Bucket": bucket, "Key": key} | ||
| response = s3ec.get_object(**get_req) | ||
|
|
||
| # Read raw bytes without decoding | ||
| output = response["Body"].read() | ||
|
|
||
| if output != data: | ||
| print("Uh oh! Input and output don't match!") | ||
| print("Input:") | ||
| print(repr(data)) | ||
| print("Output:") | ||
| print(repr(output)) | ||
| raise RuntimeError | ||
| print("Success! Binary data encrypted and decrypted correctly.") | ||
|
|
||
|
|
||
| def test_invalid_body_types(): | ||
| """Test that put_object raises an exception when given invalid body types.""" | ||
| key = "invalid-body-type" | ||
| key += datetime.now().strftime("%Y-%m-%d-%H:%M:%S") | ||
|
|
||
| kms_client = boto3.client("kms", region_name=region) | ||
| keyring = KmsKeyring(kms_client, kms_key_id) | ||
| wrapped_client = boto3.client("s3") | ||
| config = S3EncryptionClientConfig(keyring) | ||
| s3ec = S3EncryptionClient(wrapped_client, config) | ||
|
|
||
| # Test with integer | ||
| with pytest.raises(S3EncryptionClientError) as excinfo: | ||
| s3ec.put_object(Bucket=bucket, Key=key, Body=42) | ||
| assert "not an acceptable type" in str(excinfo.value) | ||
|
|
||
| # Test with float | ||
| with pytest.raises(S3EncryptionClientError) as excinfo: | ||
| s3ec.put_object(Bucket=bucket, Key=key, Body=3.14) | ||
| assert "not an acceptable type" in str(excinfo.value) | ||
|
|
||
| # Test with list | ||
| with pytest.raises(S3EncryptionClientError) as excinfo: | ||
| s3ec.put_object(Bucket=bucket, Key=key, Body=[1, 2, 3]) | ||
| assert "not an acceptable type" in str(excinfo.value) | ||
|
|
||
| # Test with dictionary | ||
| with pytest.raises(S3EncryptionClientError) as excinfo: | ||
| s3ec.put_object(Bucket=bucket, Key=key, Body={"key": "value"}) | ||
| assert "not an acceptable type" in str(excinfo.value) | ||
|
|
||
| # Test with boolean | ||
| with pytest.raises(S3EncryptionClientError) as excinfo: | ||
| s3ec.put_object(Bucket=bucket, Key=key, Body=True) | ||
| assert "not an acceptable type" in str(excinfo.value) | ||
|
|
||
| # Test with None (also raises an exception) | ||
| with pytest.raises(S3EncryptionClientError) as excinfo: | ||
| s3ec.put_object(Bucket=bucket, Key=key, Body=None) | ||
| assert "not an acceptable type" in str(excinfo.value) | ||
|
|
||
| print("Success! All invalid body types correctly raised exceptions.") | ||
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.
Unit testing this could be annoying due to global module -- maybe have the test inject a new serializer into the module then remove it?