Skip to content

Cloud Storage

  • Client: google-cloud-storage
  • Transport: JSON API (the client default). drongo intercepts it directly.
  • Backend: global namespace (buckets are shared across projects, exactly as moto special-cases S3).

Use the normal client with no changes.

Buckets and objects

from drongo import mock_gcp


@mock_gcp
def test_upload_download():
    from google.cloud import storage

    client = storage.Client(project="my-project")
    bucket = client.create_bucket("my-bucket")

    bucket.blob("hello.txt").upload_from_string(
        "hello drongo", content_type="text/plain"
    )

    assert bucket.blob("hello.txt").download_as_text() == "hello drongo"
    assert [b.name for b in client.list_blobs("my-bucket")] == ["hello.txt"]

Uploads: simple, multipart, resumable

The client picks an upload strategy based on payload size and arguments. drongo handles all three transparently:

@mock_gcp
def test_uploads():
    from google.cloud import storage

    client = storage.Client(project="p")
    bucket = client.create_bucket("b")

    # Simple/media upload
    bucket.blob("small.txt").upload_from_string("small")

    # Multipart upload (metadata + media in one request)
    blob = bucket.blob("meta.txt")
    blob.metadata = {"team": "payments"}
    blob.upload_from_string("with metadata", content_type="text/plain")

    # Resumable upload (large payloads, chunked)
    big = bucket.blob("big.bin")
    big.chunk_size = 256 * 1024
    big.upload_from_string(b"x" * (1024 * 1024))

    assert bucket.blob("big.bin").size == 1024 * 1024

Ranged downloads

@mock_gcp
def test_range():
    from google.cloud import storage

    client = storage.Client(project="p")
    bucket = client.create_bucket("b")
    bucket.blob("data.txt").upload_from_string("abcdefghij")

    assert bucket.blob("data.txt").download_as_bytes(start=2, end=4) == b"cde"

Listing with prefix and delimiter

delimiter="/" gives you S3-style "folders" via prefixes:

@mock_gcp
def test_list_prefixes():
    from google.cloud import storage

    client = storage.Client(project="p")
    bucket = client.create_bucket("b")
    for name in ["a/1.txt", "a/2.txt", "b/3.txt", "top.txt"]:
        bucket.blob(name).upload_from_string("x")

    it = client.list_blobs("b", prefix="a/", delimiter="/")
    assert [blob.name for blob in it] == ["a/1.txt", "a/2.txt"]

Copy and rewrite

@mock_gcp
def test_copy():
    from google.cloud import storage

    client = storage.Client(project="p")
    src = client.create_bucket("src")
    dst = client.create_bucket("dst")
    src.blob("orig.txt").upload_from_string("payload")

    src.copy_blob(src.blob("orig.txt"), dst, "copy.txt")
    assert dst.blob("copy.txt").download_as_text() == "payload"

Inspecting state

from drongo import get_backend, mock_gcp


@mock_gcp
def test_inspect():
    from google.cloud import storage

    storage.Client(project="p").create_bucket("b")
    # Storage is global-namespace, so any project key returns the shared backend.
    assert "b" in get_backend("storage")["p"].buckets

Coverage

Operation Status
Create / get / list / delete bucket Supported
Bucket metadata (location, labels, versioning) Partial
Upload: simple (media) Supported
Upload: multipart Supported
Upload: resumable (single + chunked) Supported
Download (full) Supported
Download with Range Supported
Object metadata get / patch Supported
List objects (prefix + delimiter) Supported
Delete object Supported
Copy / rewrite object Supported
Signed URLs, ACLs, IAM, notifications Planned