chore: import upstream snapshot with attribution
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.aliyun import AliyunConfig, AliyunResource
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_aliyun_regional_endpoint():
|
||||
config = AliyunConfig(bucket="b",
|
||||
region="cn-hangzhou",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == (
|
||||
"https://s3.oss-cn-hangzhou.aliyuncs.com")
|
||||
|
||||
|
||||
def test_aliyun_requires_region():
|
||||
with pytest.raises(ValidationError):
|
||||
AliyunConfig(bucket="b", access_key_id="k", secret_access_key="s")
|
||||
|
||||
|
||||
def test_aliyun_custom_endpoint_override():
|
||||
config = AliyunConfig(bucket="b",
|
||||
region="cn-beijing",
|
||||
endpoint_url="https://custom.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == "https://custom.example.com"
|
||||
|
||||
|
||||
def test_aliyun_to_s3_config():
|
||||
config = AliyunConfig(bucket="b",
|
||||
region="us-west-1",
|
||||
access_key_id="key",
|
||||
secret_access_key="secret")
|
||||
s3 = config.to_s3_config()
|
||||
assert isinstance(s3, S3Config)
|
||||
assert s3.endpoint_url == "https://s3.oss-us-west-1.aliyuncs.com"
|
||||
assert reveal_secret(s3.aws_secret_access_key) == "secret"
|
||||
|
||||
|
||||
def test_aliyun_resource_uses_s3_resource_type():
|
||||
resource = AliyunResource(
|
||||
AliyunConfig(bucket="b",
|
||||
region="cn-hangzhou",
|
||||
access_key_id="k",
|
||||
secret_access_key="s"))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert isinstance(resource.config, S3Config)
|
||||
@@ -0,0 +1,65 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.backblaze import BackblazeConfig, BackblazeResource
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_backblaze_regional_endpoint():
|
||||
config = BackblazeConfig(bucket="b",
|
||||
region="us-west-002",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == (
|
||||
"https://s3.us-west-002.backblazeb2.com")
|
||||
|
||||
|
||||
def test_backblaze_requires_region():
|
||||
with pytest.raises(ValidationError):
|
||||
BackblazeConfig(bucket="b", access_key_id="k", secret_access_key="s")
|
||||
|
||||
|
||||
def test_backblaze_custom_endpoint_override():
|
||||
config = BackblazeConfig(bucket="b",
|
||||
region="us-west-002",
|
||||
endpoint_url="https://custom.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == "https://custom.example.com"
|
||||
|
||||
|
||||
def test_backblaze_to_s3_config():
|
||||
config = BackblazeConfig(bucket="b",
|
||||
region="eu-central-003",
|
||||
access_key_id="key",
|
||||
secret_access_key="secret")
|
||||
s3 = config.to_s3_config()
|
||||
assert isinstance(s3, S3Config)
|
||||
assert s3.endpoint_url == "https://s3.eu-central-003.backblazeb2.com"
|
||||
assert reveal_secret(s3.aws_secret_access_key) == "secret"
|
||||
|
||||
|
||||
def test_backblaze_resource_uses_s3_resource_type():
|
||||
resource = BackblazeResource(
|
||||
BackblazeConfig(bucket="b",
|
||||
region="us-west-002",
|
||||
access_key_id="k",
|
||||
secret_access_key="s"))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert isinstance(resource.config, S3Config)
|
||||
@@ -0,0 +1,71 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.ceph import CephConfig, CephResource
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_ceph_config_defaults():
|
||||
config = CephConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="https://ceph.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
)
|
||||
assert config.region == "us-east-1"
|
||||
assert config.path_style is True
|
||||
|
||||
|
||||
def test_ceph_config_immutable():
|
||||
config = CephConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="https://ceph.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
)
|
||||
with pytest.raises(ValidationError):
|
||||
config.bucket = "other"
|
||||
|
||||
|
||||
def test_ceph_config_to_s3_config():
|
||||
config = CephConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="https://ceph.example.com",
|
||||
access_key_id="key",
|
||||
secret_access_key="secret",
|
||||
)
|
||||
s3 = config.to_s3_config()
|
||||
assert isinstance(s3, S3Config)
|
||||
assert s3.endpoint_url == "https://ceph.example.com"
|
||||
assert s3.path_style is True
|
||||
assert reveal_secret(s3.aws_access_key_id) == "key"
|
||||
assert reveal_secret(s3.aws_secret_access_key) == "secret"
|
||||
|
||||
|
||||
def test_ceph_resource_uses_s3_resource_type():
|
||||
resource = CephResource(
|
||||
CephConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="https://ceph.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert isinstance(resource.config, S3Config)
|
||||
assert resource.ceph_config.endpoint_url == "https://ceph.example.com"
|
||||
@@ -0,0 +1,68 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.digitalocean import (DigitalOceanConfig,
|
||||
DigitalOceanResource)
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_do_regional_endpoint():
|
||||
config = DigitalOceanConfig(bucket="b",
|
||||
region="nyc3",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == (
|
||||
"https://nyc3.digitaloceanspaces.com")
|
||||
|
||||
|
||||
def test_do_requires_region():
|
||||
with pytest.raises(ValidationError):
|
||||
DigitalOceanConfig(bucket="b",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
|
||||
|
||||
def test_do_custom_endpoint_override():
|
||||
config = DigitalOceanConfig(bucket="b",
|
||||
region="nyc3",
|
||||
endpoint_url="https://custom.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == "https://custom.example.com"
|
||||
|
||||
|
||||
def test_do_to_s3_config():
|
||||
config = DigitalOceanConfig(bucket="b",
|
||||
region="fra1",
|
||||
access_key_id="key",
|
||||
secret_access_key="secret")
|
||||
s3 = config.to_s3_config()
|
||||
assert isinstance(s3, S3Config)
|
||||
assert s3.endpoint_url == "https://fra1.digitaloceanspaces.com"
|
||||
assert reveal_secret(s3.aws_access_key_id) == "key"
|
||||
|
||||
|
||||
def test_do_resource_uses_s3_resource_type():
|
||||
resource = DigitalOceanResource(
|
||||
DigitalOceanConfig(bucket="b",
|
||||
region="sfo3",
|
||||
access_key_id="k",
|
||||
secret_access_key="s"))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert isinstance(resource.config, S3Config)
|
||||
@@ -0,0 +1,78 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.gcs import GCSConfig, GCSResource
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
|
||||
|
||||
def test_gcs_config_defaults():
|
||||
c = GCSConfig(
|
||||
bucket="my-bucket",
|
||||
access_key_id="GOOG123",
|
||||
secret_access_key="secret",
|
||||
)
|
||||
assert c.endpoint_url == "https://storage.googleapis.com"
|
||||
assert c.region == "auto"
|
||||
assert c.timeout == 30
|
||||
assert c.proxy is None
|
||||
|
||||
|
||||
def test_gcs_config_immutable():
|
||||
c = GCSConfig(
|
||||
bucket="x",
|
||||
access_key_id="GOOG123",
|
||||
secret_access_key="secret",
|
||||
)
|
||||
with pytest.raises(ValidationError):
|
||||
c.bucket = "y"
|
||||
|
||||
|
||||
def test_gcs_config_custom_endpoint():
|
||||
c = GCSConfig(
|
||||
bucket="my-bucket",
|
||||
access_key_id="GOOG123",
|
||||
secret_access_key="secret",
|
||||
endpoint_url="https://custom.endpoint.com",
|
||||
)
|
||||
assert c.endpoint_url == "https://custom.endpoint.com"
|
||||
|
||||
|
||||
def test_gcs_to_s3_config():
|
||||
c = GCSConfig(
|
||||
bucket="my-bucket",
|
||||
access_key_id="GOOG123",
|
||||
secret_access_key="secret",
|
||||
)
|
||||
s3c = c.to_s3_config()
|
||||
assert s3c.bucket == "my-bucket"
|
||||
assert reveal_secret(s3c.aws_access_key_id) == "GOOG123"
|
||||
assert reveal_secret(s3c.aws_secret_access_key) == "secret"
|
||||
assert s3c.endpoint_url == "https://storage.googleapis.com"
|
||||
assert s3c.region == "auto"
|
||||
|
||||
|
||||
def test_gcs_resource_creates():
|
||||
c = GCSConfig(
|
||||
bucket="my-bucket",
|
||||
access_key_id="GOOG123",
|
||||
secret_access_key="secret",
|
||||
)
|
||||
resource = GCSResource(c)
|
||||
assert resource.gcs_config is c
|
||||
assert resource.accessor.config.bucket == "my-bucket"
|
||||
expected = "https://storage.googleapis.com"
|
||||
assert resource.accessor.config.endpoint_url == expected
|
||||
@@ -0,0 +1,44 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.hf_buckets import HfBucketsConfig, HfBucketsResource
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_resource_name():
|
||||
r = HfBucketsResource(HfBucketsConfig(bucket="o/b"))
|
||||
assert r.name == ResourceName.HF_BUCKETS
|
||||
assert r.caches_reads is True
|
||||
assert r.SUPPORTS_SNAPSHOT is True
|
||||
|
||||
|
||||
def test_config_immutable():
|
||||
cfg = HfBucketsConfig(bucket="o/b")
|
||||
with pytest.raises(ValidationError):
|
||||
cfg.bucket = "other/other"
|
||||
|
||||
|
||||
def test_resource_registers_ops():
|
||||
r = HfBucketsResource(HfBucketsConfig(bucket="o/b"))
|
||||
op_names = {o.name for o in r.ops_list()}
|
||||
assert {"read", "readdir", "stat", "write", "create", "unlink"} <= op_names
|
||||
|
||||
|
||||
def test_resource_registers_commands():
|
||||
r = HfBucketsResource(HfBucketsConfig(bucket="o/b"))
|
||||
cmd_names = {c.name for c in r.commands()}
|
||||
assert {"cat", "ls", "grep", "stat", "touch", "rm"} <= cmd_names
|
||||
@@ -0,0 +1,44 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.hf_datasets import HfDatasetsConfig, HfDatasetsResource
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_resource_name():
|
||||
r = HfDatasetsResource(HfDatasetsConfig(repo_id="org/dataset"))
|
||||
assert r.name == ResourceName.HF_DATASETS
|
||||
assert r.caches_reads is True
|
||||
assert r.SUPPORTS_SNAPSHOT is True
|
||||
|
||||
|
||||
def test_config_immutable():
|
||||
cfg = HfDatasetsConfig(repo_id="org/dataset")
|
||||
with pytest.raises(ValidationError):
|
||||
cfg.repo_id = "other/other"
|
||||
|
||||
|
||||
def test_resource_registers_ops():
|
||||
r = HfDatasetsResource(HfDatasetsConfig(repo_id="org/dataset"))
|
||||
op_names = {o.name for o in r.ops_list()}
|
||||
assert {"read", "readdir", "stat", "write", "create", "unlink"} <= op_names
|
||||
|
||||
|
||||
def test_resource_registers_commands():
|
||||
r = HfDatasetsResource(HfDatasetsConfig(repo_id="org/dataset"))
|
||||
cmd_names = {c.name for c in r.commands()}
|
||||
assert {"cat", "ls", "grep", "stat", "touch", "rm"} <= cmd_names
|
||||
@@ -0,0 +1,39 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.hf_models import HfModelsConfig, HfModelsResource
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_resource_name():
|
||||
r = HfModelsResource(HfModelsConfig(repo_id="org/model"))
|
||||
assert r.name == ResourceName.HF_MODELS
|
||||
assert r.caches_reads is True
|
||||
|
||||
|
||||
def test_config_immutable():
|
||||
cfg = HfModelsConfig(repo_id="org/model")
|
||||
with pytest.raises(ValidationError):
|
||||
cfg.repo_id = "other/other"
|
||||
|
||||
|
||||
def test_resource_registers_ops_and_commands():
|
||||
r = HfModelsResource(HfModelsConfig(repo_id="org/model"))
|
||||
op_names = {o.name for o in r.ops_list()}
|
||||
cmd_names = {c.name for c in r.commands()}
|
||||
assert {"read", "readdir", "stat"} <= op_names
|
||||
assert {"cat", "ls", "stat"} <= cmd_names
|
||||
@@ -0,0 +1,39 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.hf_spaces import HfSpacesConfig, HfSpacesResource
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_resource_name():
|
||||
r = HfSpacesResource(HfSpacesConfig(repo_id="org/space"))
|
||||
assert r.name == ResourceName.HF_SPACES
|
||||
assert r.caches_reads is True
|
||||
|
||||
|
||||
def test_config_immutable():
|
||||
cfg = HfSpacesConfig(repo_id="org/space")
|
||||
with pytest.raises(ValidationError):
|
||||
cfg.repo_id = "other/other"
|
||||
|
||||
|
||||
def test_resource_registers_ops_and_commands():
|
||||
r = HfSpacesResource(HfSpacesConfig(repo_id="org/space"))
|
||||
op_names = {o.name for o in r.ops_list()}
|
||||
cmd_names = {c.name for c in r.commands()}
|
||||
assert {"read", "readdir", "stat"} <= op_names
|
||||
assert {"cat", "ls", "stat"} <= cmd_names
|
||||
@@ -0,0 +1,97 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.minio import MinIOConfig, MinIOResource
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_minio_config_defaults():
|
||||
config = MinIOConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="http://localhost:9000",
|
||||
access_key_id="minioadmin",
|
||||
secret_access_key="minioadmin",
|
||||
)
|
||||
assert config.region == "us-east-1"
|
||||
assert config.path_style is True
|
||||
assert config.timeout == 30
|
||||
|
||||
|
||||
def test_minio_config_immutable():
|
||||
config = MinIOConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="http://localhost:9000",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
)
|
||||
with pytest.raises(ValidationError):
|
||||
config.bucket = "other"
|
||||
|
||||
|
||||
def test_minio_config_to_s3_config():
|
||||
config = MinIOConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="http://localhost:9000",
|
||||
access_key_id="minioadmin",
|
||||
secret_access_key="minioadmin",
|
||||
proxy="http://localhost:8080",
|
||||
)
|
||||
s3 = config.to_s3_config()
|
||||
assert isinstance(s3, S3Config)
|
||||
assert s3.bucket == "my-bucket"
|
||||
assert s3.endpoint_url == "http://localhost:9000"
|
||||
assert s3.path_style is True
|
||||
assert reveal_secret(s3.aws_access_key_id) == "minioadmin"
|
||||
assert reveal_secret(s3.aws_secret_access_key) == "minioadmin"
|
||||
assert s3.proxy == "http://localhost:8080"
|
||||
|
||||
|
||||
def test_minio_config_path_style_override():
|
||||
config = MinIOConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="https://minio.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
path_style=False,
|
||||
)
|
||||
assert config.to_s3_config().path_style is False
|
||||
|
||||
|
||||
def test_minio_resource_uses_s3_resource_type():
|
||||
resource = MinIOResource(
|
||||
MinIOConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="http://localhost:9000",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert resource.caches_reads is True
|
||||
assert isinstance(resource.config, S3Config)
|
||||
|
||||
|
||||
def test_minio_resource_preserves_original_config():
|
||||
config = MinIOConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="http://localhost:9000",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
)
|
||||
resource = MinIOResource(config)
|
||||
assert resource.minio_config is config
|
||||
@@ -0,0 +1,108 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.oci import OCIConfig, OCIResource
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_oci_config_defaults():
|
||||
config = OCIConfig(
|
||||
bucket="my-bucket",
|
||||
namespace="my-namespace",
|
||||
region="us-ashburn-1",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
)
|
||||
assert config.timeout == 30
|
||||
assert config.resolved_endpoint_url() == (
|
||||
"https://my-namespace.compat.objectstorage."
|
||||
"us-ashburn-1.oci.customer-oci.com")
|
||||
|
||||
|
||||
def test_oci_config_immutable():
|
||||
config = OCIConfig(
|
||||
bucket="my-bucket",
|
||||
namespace="my-namespace",
|
||||
region="us-ashburn-1",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
)
|
||||
with pytest.raises(ValidationError):
|
||||
config.bucket = "other-bucket"
|
||||
|
||||
|
||||
def test_oci_config_to_s3_config():
|
||||
config = OCIConfig(
|
||||
bucket="my-bucket",
|
||||
namespace="my-namespace",
|
||||
region="us-ashburn-1",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
proxy="http://localhost:8080",
|
||||
)
|
||||
s3_config = config.to_s3_config()
|
||||
assert isinstance(s3_config, S3Config)
|
||||
assert s3_config.bucket == "my-bucket"
|
||||
assert s3_config.region == "us-ashburn-1"
|
||||
assert s3_config.endpoint_url == (
|
||||
"https://my-namespace.compat.objectstorage."
|
||||
"us-ashburn-1.oci.customer-oci.com")
|
||||
assert reveal_secret(s3_config.aws_access_key_id) == "access-key"
|
||||
assert reveal_secret(s3_config.aws_secret_access_key) == "secret-key"
|
||||
assert s3_config.path_style is True
|
||||
assert s3_config.proxy == "http://localhost:8080"
|
||||
|
||||
|
||||
def test_oci_config_custom_endpoint():
|
||||
config = OCIConfig(
|
||||
bucket="my-bucket",
|
||||
namespace="my-namespace",
|
||||
region="us-ashburn-1",
|
||||
endpoint_url="https://custom.example.com",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
)
|
||||
assert config.resolved_endpoint_url() == "https://custom.example.com"
|
||||
|
||||
|
||||
def test_oci_resource_uses_s3_resource_type():
|
||||
resource = OCIResource(
|
||||
OCIConfig(
|
||||
bucket="my-bucket",
|
||||
namespace="my-namespace",
|
||||
region="us-ashburn-1",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert resource.caches_reads is True
|
||||
assert isinstance(resource.config, S3Config)
|
||||
assert resource.config.path_style is True
|
||||
|
||||
|
||||
def test_oci_resource_preserves_original_config():
|
||||
config = OCIConfig(
|
||||
bucket="my-bucket",
|
||||
namespace="my-namespace",
|
||||
region="us-ashburn-1",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
)
|
||||
resource = OCIResource(config)
|
||||
assert resource.oci_config is config
|
||||
@@ -0,0 +1,64 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.qingstor import QingStorConfig, QingStorResource
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_qingstor_zone_endpoint():
|
||||
config = QingStorConfig(bucket="b",
|
||||
region="pek3b",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == "https://s3.pek3b.qingstor.com"
|
||||
|
||||
|
||||
def test_qingstor_requires_region():
|
||||
with pytest.raises(ValidationError):
|
||||
QingStorConfig(bucket="b", access_key_id="k", secret_access_key="s")
|
||||
|
||||
|
||||
def test_qingstor_custom_endpoint_override():
|
||||
config = QingStorConfig(bucket="b",
|
||||
region="sh1a",
|
||||
endpoint_url="https://custom.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == "https://custom.example.com"
|
||||
|
||||
|
||||
def test_qingstor_to_s3_config():
|
||||
config = QingStorConfig(bucket="b",
|
||||
region="gd2a",
|
||||
access_key_id="key",
|
||||
secret_access_key="secret")
|
||||
s3 = config.to_s3_config()
|
||||
assert isinstance(s3, S3Config)
|
||||
assert s3.endpoint_url == "https://s3.gd2a.qingstor.com"
|
||||
assert reveal_secret(s3.aws_access_key_id) == "key"
|
||||
|
||||
|
||||
def test_qingstor_resource_uses_s3_resource_type():
|
||||
resource = QingStorResource(
|
||||
QingStorConfig(bucket="b",
|
||||
region="pek3b",
|
||||
access_key_id="k",
|
||||
secret_access_key="s"))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert isinstance(resource.config, S3Config)
|
||||
@@ -0,0 +1,84 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.r2 import R2Config, R2Resource
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_r2config_defaults():
|
||||
config = R2Config(bucket="my-bucket", account_id="account-123")
|
||||
assert config.region == "auto"
|
||||
assert config.timeout == 30
|
||||
assert config.resolved_endpoint_url() == (
|
||||
"https://account-123.r2.cloudflarestorage.com")
|
||||
|
||||
|
||||
def test_r2config_immutable():
|
||||
config = R2Config(bucket="my-bucket", account_id="account-123")
|
||||
with pytest.raises(ValidationError):
|
||||
config.bucket = "other-bucket"
|
||||
|
||||
|
||||
def test_r2config_to_s3_config():
|
||||
config = R2Config(
|
||||
bucket="my-bucket",
|
||||
account_id="account-123",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
proxy="http://localhost:8080",
|
||||
)
|
||||
s3_config = config.to_s3_config()
|
||||
assert isinstance(s3_config, S3Config)
|
||||
assert s3_config.bucket == "my-bucket"
|
||||
assert s3_config.region == "auto"
|
||||
assert s3_config.endpoint_url == (
|
||||
"https://account-123.r2.cloudflarestorage.com")
|
||||
assert reveal_secret(s3_config.aws_access_key_id) == "access-key"
|
||||
assert reveal_secret(s3_config.aws_secret_access_key) == "secret-key"
|
||||
assert s3_config.proxy == "http://localhost:8080"
|
||||
|
||||
|
||||
def test_r2config_custom_endpoint():
|
||||
config = R2Config(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="https://custom.example.com",
|
||||
)
|
||||
assert config.resolved_endpoint_url() == "https://custom.example.com"
|
||||
|
||||
|
||||
def test_r2config_requires_account_id_or_endpoint():
|
||||
config = R2Config(bucket="my-bucket")
|
||||
with pytest.raises(ValueError):
|
||||
config.resolved_endpoint_url()
|
||||
|
||||
|
||||
def test_r2resource_uses_s3_resource_type():
|
||||
resource = R2Resource(
|
||||
R2Config(bucket="my-bucket", account_id="account-123"))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert resource.caches_reads is True
|
||||
assert isinstance(resource.config, S3Config)
|
||||
assert resource.config.endpoint_url == (
|
||||
"https://account-123.r2.cloudflarestorage.com")
|
||||
|
||||
|
||||
def test_r2resource_preserves_original_config():
|
||||
config = R2Config(bucket="my-bucket", account_id="account-123")
|
||||
resource = R2Resource(config)
|
||||
assert resource.r2_config is config
|
||||
@@ -0,0 +1,79 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.s3 import S3Config
|
||||
|
||||
|
||||
def test_s3config_defaults():
|
||||
c = S3Config(bucket="my-bucket")
|
||||
assert c.region is None
|
||||
assert c.timeout == 30
|
||||
|
||||
|
||||
def test_s3config_immutable():
|
||||
c = S3Config(bucket="x")
|
||||
with pytest.raises(ValidationError):
|
||||
c.bucket = "y"
|
||||
|
||||
|
||||
def test_s3_write_commands_tagged():
|
||||
from mirage.commands.builtin.s3 import COMMANDS
|
||||
write_names = {
|
||||
"rm",
|
||||
"mkdir",
|
||||
"touch",
|
||||
"cp",
|
||||
"mv",
|
||||
"ln",
|
||||
"tee",
|
||||
"mktemp",
|
||||
"split",
|
||||
"csplit",
|
||||
"gzip",
|
||||
"gunzip",
|
||||
"zip",
|
||||
"unzip",
|
||||
"tar",
|
||||
"patch",
|
||||
"iconv",
|
||||
}
|
||||
for fn in COMMANDS:
|
||||
for rc in fn._registered_commands:
|
||||
if rc.name in write_names:
|
||||
assert rc.write is True, (f"{rc.name} should be write=True")
|
||||
else:
|
||||
assert rc.write is False, (f"{rc.name} should be write=False")
|
||||
|
||||
|
||||
def test_s3_write_ops_tagged():
|
||||
from mirage.ops.s3 import OPS
|
||||
write_op_names = {
|
||||
"write",
|
||||
"unlink",
|
||||
"rmdir",
|
||||
"mkdir",
|
||||
"create",
|
||||
"truncate",
|
||||
"rename",
|
||||
}
|
||||
for fn in OPS:
|
||||
for ro in fn._registered_ops:
|
||||
if ro.name in write_op_names:
|
||||
assert ro.write is True, (f"op {ro.name} should be write=True")
|
||||
else:
|
||||
assert ro.write is False, (
|
||||
f"op {ro.name} should be write=False")
|
||||
@@ -0,0 +1,72 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import re
|
||||
from io import BytesIO
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from mirage.resource.s3 import S3Config, S3Resource
|
||||
|
||||
|
||||
def _mock_backend(data: bytes) -> S3Resource:
|
||||
config = S3Config(
|
||||
bucket="test-bucket",
|
||||
region="us-east-1",
|
||||
aws_access_key_id="fake",
|
||||
aws_secret_access_key="fake",
|
||||
)
|
||||
backend = S3Resource.__new__(S3Resource)
|
||||
backend.config = config
|
||||
backend._prefix = ""
|
||||
backend._event_handlers = []
|
||||
|
||||
mock_client = MagicMock()
|
||||
body = BytesIO(data)
|
||||
mock_client.get_object.return_value = {"Body": body}
|
||||
backend._client = mock_client
|
||||
return backend
|
||||
|
||||
|
||||
def test_grep_file_uses_streaming():
|
||||
from mirage.commands.builtin.grep_helper import grep_lines
|
||||
data = b"hello world\nfoo bar\nhello again\n"
|
||||
compiled = re.compile("hello")
|
||||
results = grep_lines("test.txt",
|
||||
data.decode(errors="replace").splitlines(),
|
||||
compiled,
|
||||
invert=False,
|
||||
line_numbers=False,
|
||||
count_only=False,
|
||||
files_only=False,
|
||||
only_matching=False,
|
||||
max_count=None)
|
||||
assert len(results) == 2
|
||||
assert "hello world" in results[0]
|
||||
|
||||
|
||||
def test_grep_file_max_count():
|
||||
from mirage.commands.builtin.grep_helper import grep_lines
|
||||
lines = [f"match line {i}".encode() for i in range(100)]
|
||||
data = b"\n".join(lines) + b"\n"
|
||||
compiled = re.compile("match")
|
||||
results = grep_lines("test.txt",
|
||||
data.decode(errors="replace").splitlines(),
|
||||
compiled,
|
||||
invert=False,
|
||||
line_numbers=False,
|
||||
count_only=False,
|
||||
files_only=False,
|
||||
only_matching=False,
|
||||
max_count=3)
|
||||
assert len(results) == 3
|
||||
@@ -0,0 +1,293 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.accessor.s3 import S3Accessor
|
||||
from mirage.cache.index import IndexEntry
|
||||
from mirage.cache.index.ram import RAMIndexCacheStore
|
||||
from mirage.core.s3.readdir import readdir
|
||||
from mirage.core.s3.stat import stat
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.types import FileType, PathSpec
|
||||
from mirage.utils.key_prefix import mount_key
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def s3_config():
|
||||
return S3Config(bucket="test-bucket", region="us-east-1")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def s3_accessor(s3_config):
|
||||
return S3Accessor(s3_config)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def index():
|
||||
return RAMIndexCacheStore()
|
||||
|
||||
|
||||
def _mock_client_ctx(mock_client):
|
||||
mock_ctx = AsyncMock()
|
||||
mock_ctx.__aenter__.return_value = mock_client
|
||||
return mock_ctx
|
||||
|
||||
|
||||
class TestStatDirectoryFallback:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_file_returns_filestat(self, s3_accessor):
|
||||
with patch("mirage.core.s3.stat.async_session") as mock_session:
|
||||
mock_client = AsyncMock()
|
||||
mock_client.head_object.return_value = {
|
||||
"ContentLength": 1024,
|
||||
"LastModified": datetime(2026, 1, 1, tzinfo=timezone.utc),
|
||||
"ETag": '"abc123"',
|
||||
}
|
||||
mock_session.return_value.client.return_value = _mock_client_ctx(
|
||||
mock_client)
|
||||
|
||||
path = PathSpec(resource_path=mount_key("/s3/data/file.txt",
|
||||
"/s3"),
|
||||
virtual="/s3/data/file.txt",
|
||||
directory="/s3/data/")
|
||||
result = await stat(s3_accessor, path)
|
||||
assert result.name == "file.txt"
|
||||
assert result.size == 1024
|
||||
assert result.type == FileType.TEXT
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_directory_prefix_fallback(self, s3_accessor):
|
||||
with patch("mirage.core.s3.stat.async_session") as mock_session:
|
||||
mock_client = AsyncMock()
|
||||
err = Exception("not found")
|
||||
err.response = {"Error": {"Code": "404"}}
|
||||
mock_client.head_object.side_effect = err
|
||||
mock_client.list_objects_v2.return_value = {
|
||||
"CommonPrefixes": [{
|
||||
"Prefix": "data/subdir/"
|
||||
}],
|
||||
}
|
||||
mock_session.return_value.client.return_value = _mock_client_ctx(
|
||||
mock_client)
|
||||
|
||||
path = PathSpec(resource_path=mount_key("/s3/data", "/s3"),
|
||||
virtual="/s3/data",
|
||||
directory="/s3/")
|
||||
result = await stat(s3_accessor, path)
|
||||
assert result.name == "data"
|
||||
assert result.type == FileType.DIRECTORY
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_directory_prefix_with_contents(self, s3_accessor):
|
||||
with patch("mirage.core.s3.stat.async_session") as mock_session:
|
||||
mock_client = AsyncMock()
|
||||
err = Exception("not found")
|
||||
err.response = {"Error": {"Code": "NoSuchKey"}}
|
||||
mock_client.head_object.side_effect = err
|
||||
mock_client.list_objects_v2.return_value = {
|
||||
"Contents": [{
|
||||
"Key": "data/file.txt"
|
||||
}],
|
||||
}
|
||||
mock_session.return_value.client.return_value = _mock_client_ctx(
|
||||
mock_client)
|
||||
|
||||
path = PathSpec(resource_path=mount_key("/s3/data", "/s3"),
|
||||
virtual="/s3/data",
|
||||
directory="/s3/")
|
||||
result = await stat(s3_accessor, path)
|
||||
assert result.name == "data"
|
||||
assert result.type == FileType.DIRECTORY
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_nonexistent_raises(self, s3_accessor):
|
||||
with patch("mirage.core.s3.stat.async_session") as mock_session:
|
||||
mock_client = AsyncMock()
|
||||
err = Exception("not found")
|
||||
err.response = {"Error": {"Code": "404"}}
|
||||
mock_client.head_object.side_effect = err
|
||||
mock_client.list_objects_v2.return_value = {}
|
||||
mock_session.return_value.client.return_value = _mock_client_ctx(
|
||||
mock_client)
|
||||
|
||||
path = PathSpec(resource_path=mount_key("/s3/nope", "/s3"),
|
||||
virtual="/s3/nope",
|
||||
directory="/s3/")
|
||||
with pytest.raises(FileNotFoundError):
|
||||
await stat(s3_accessor, path)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_root_returns_directory(self, s3_accessor):
|
||||
path = PathSpec(resource_path=mount_key("/s3/", "/s3"),
|
||||
virtual="/s3/",
|
||||
directory="/s3/")
|
||||
result = await stat(s3_accessor, path)
|
||||
assert result.name == "/"
|
||||
assert result.type == FileType.DIRECTORY
|
||||
|
||||
|
||||
class TestStatIndexCache:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_uses_index_for_folder(self, s3_accessor, index):
|
||||
await index.put(
|
||||
"/s3/data",
|
||||
IndexEntry(id="/data", name="data", resource_type="folder"))
|
||||
|
||||
path = PathSpec(resource_path=mount_key("/s3/data", "/s3"),
|
||||
virtual="/s3/data",
|
||||
directory="/s3/")
|
||||
result = await stat(s3_accessor, path, index)
|
||||
assert result.name == "data"
|
||||
assert result.type == FileType.DIRECTORY
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_uses_index_for_file(self, s3_accessor, index):
|
||||
await index.put(
|
||||
"/s3/data/file.txt",
|
||||
IndexEntry(id="/data/file.txt",
|
||||
name="file.txt",
|
||||
resource_type="file",
|
||||
size=2048))
|
||||
|
||||
path = PathSpec(resource_path=mount_key("/s3/data/file.txt", "/s3"),
|
||||
virtual="/s3/data/file.txt",
|
||||
directory="/s3/data/")
|
||||
result = await stat(s3_accessor, path, index)
|
||||
assert result.name == "file.txt"
|
||||
assert result.size == 2048
|
||||
assert result.type == FileType.TEXT
|
||||
|
||||
|
||||
class TestReaddirIndexEntries:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_readdir_stores_folder_type(self, s3_accessor, index):
|
||||
with patch("mirage.core.s3.readdir.async_session") as mock_session:
|
||||
mock_client = AsyncMock()
|
||||
page_data = {
|
||||
"CommonPrefixes": [{
|
||||
"Prefix": "subdir/data/"
|
||||
}],
|
||||
"Contents": [{
|
||||
"Key": "subdir/readme.txt",
|
||||
"Size": 100
|
||||
}],
|
||||
}
|
||||
|
||||
async def _paginate(**kwargs):
|
||||
yield page_data
|
||||
|
||||
mock_paginator = MagicMock()
|
||||
mock_paginator.paginate = _paginate
|
||||
mock_client.get_paginator = MagicMock(return_value=mock_paginator)
|
||||
mock_session.return_value.client.return_value = _mock_client_ctx(
|
||||
mock_client)
|
||||
|
||||
path = PathSpec(resource_path=mount_key("/s3/subdir", "/s3"),
|
||||
virtual="/s3/subdir",
|
||||
directory="/s3/")
|
||||
result = await readdir(s3_accessor, path, index)
|
||||
assert "/s3/subdir/data" in result
|
||||
assert "/s3/subdir/readme.txt" in result
|
||||
|
||||
lookup = await index.get("/s3/subdir/data")
|
||||
assert lookup.entry is not None
|
||||
assert lookup.entry.resource_type == "folder"
|
||||
assert lookup.entry.name == "data"
|
||||
|
||||
lookup = await index.get("/s3/subdir/readme.txt")
|
||||
assert lookup.entry is not None
|
||||
assert lookup.entry.resource_type == "file"
|
||||
assert lookup.entry.size == 100
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_readdir_cache_hit(self, s3_accessor, index):
|
||||
with patch("mirage.core.s3.readdir.async_session") as mock_session:
|
||||
mock_client = AsyncMock()
|
||||
page_data = {
|
||||
"CommonPrefixes": [{
|
||||
"Prefix": "subdir/nested/"
|
||||
}],
|
||||
"Contents": [],
|
||||
}
|
||||
|
||||
async def _paginate(**kwargs):
|
||||
yield page_data
|
||||
|
||||
mock_paginator = MagicMock()
|
||||
mock_paginator.paginate = _paginate
|
||||
mock_client.get_paginator = MagicMock(return_value=mock_paginator)
|
||||
mock_session.return_value.client.return_value = _mock_client_ctx(
|
||||
mock_client)
|
||||
|
||||
path = PathSpec(resource_path=mount_key("/s3/subdir", "/s3"),
|
||||
virtual="/s3/subdir",
|
||||
directory="/s3/")
|
||||
r1 = await readdir(s3_accessor, path, index)
|
||||
r2 = await readdir(s3_accessor, path, index)
|
||||
assert r1 == r2
|
||||
assert mock_client.get_paginator.call_count == 1
|
||||
|
||||
|
||||
class TestStatAfterReaddir:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_hits_cache_after_readdir(self, s3_accessor, index):
|
||||
with patch("mirage.core.s3.readdir.async_session") as mock_session:
|
||||
mock_client = AsyncMock()
|
||||
page_data = {
|
||||
"CommonPrefixes": [{
|
||||
"Prefix": "subdir/nested/"
|
||||
}],
|
||||
"Contents": [{
|
||||
"Key": "subdir/readme.txt",
|
||||
"Size": 500
|
||||
}],
|
||||
}
|
||||
|
||||
async def _paginate(**kwargs):
|
||||
yield page_data
|
||||
|
||||
mock_paginator = MagicMock()
|
||||
mock_paginator.paginate = _paginate
|
||||
mock_client.get_paginator = MagicMock(return_value=mock_paginator)
|
||||
mock_session.return_value.client.return_value = _mock_client_ctx(
|
||||
mock_client)
|
||||
|
||||
path = PathSpec(resource_path=mount_key("/s3/subdir", "/s3"),
|
||||
virtual="/s3/subdir",
|
||||
directory="/s3/")
|
||||
await readdir(s3_accessor, path, index)
|
||||
|
||||
result = await stat(
|
||||
s3_accessor,
|
||||
PathSpec(resource_path=mount_key("/s3/subdir/nested", "/s3"),
|
||||
virtual="/s3/subdir/nested",
|
||||
directory="/s3/subdir/"), index)
|
||||
assert result.name == "nested"
|
||||
assert result.type == FileType.DIRECTORY
|
||||
|
||||
result = await stat(
|
||||
s3_accessor,
|
||||
PathSpec(resource_path=mount_key("/s3/subdir/readme.txt", "/s3"),
|
||||
virtual="/s3/subdir/readme.txt",
|
||||
directory="/s3/subdir/"), index)
|
||||
assert result.name == "readme.txt"
|
||||
assert result.size == 500
|
||||
@@ -0,0 +1,101 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.accessor.s3 import S3Accessor
|
||||
from mirage.resource.s3 import S3Config, S3Resource
|
||||
from mirage.types import PathSpec
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def s3_config():
|
||||
return S3Config(bucket="test-bucket", region="us-east-1")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def s3_accessor(s3_config):
|
||||
return S3Accessor(s3_config)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def s3_backend(s3_config):
|
||||
return S3Resource(s3_config)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_bytes(s3_accessor):
|
||||
from mirage.core.s3.read import read_bytes
|
||||
|
||||
with patch("mirage.core.s3.read.async_session") as mock_session:
|
||||
mock_client = AsyncMock()
|
||||
mock_body = AsyncMock()
|
||||
mock_body.read.return_value = b"hello world\nfoo bar\nbaz"
|
||||
mock_client.get_object.return_value = {"Body": mock_body}
|
||||
mock_ctx = AsyncMock()
|
||||
mock_ctx.__aenter__.return_value = mock_client
|
||||
mock_session.return_value.client.return_value = mock_ctx
|
||||
data = await read_bytes(
|
||||
s3_accessor,
|
||||
PathSpec(resource_path="data/file.txt",
|
||||
virtual="/data/file.txt",
|
||||
directory="/data/file.txt"))
|
||||
assert data == b"hello world\nfoo bar\nbaz"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_range_get(s3_accessor):
|
||||
from mirage.core.s3.read import read_bytes
|
||||
|
||||
with patch("mirage.core.s3.read.async_session") as mock_session:
|
||||
mock_client = AsyncMock()
|
||||
mock_body = AsyncMock()
|
||||
mock_body.read.return_value = b"hello"
|
||||
mock_client.get_object.return_value = {"Body": mock_body}
|
||||
mock_ctx = AsyncMock()
|
||||
mock_ctx.__aenter__.return_value = mock_client
|
||||
mock_session.return_value.client.return_value = mock_ctx
|
||||
data = await read_bytes(s3_accessor,
|
||||
PathSpec(resource_path="data/file.txt",
|
||||
virtual="/data/file.txt",
|
||||
directory="/data/file.txt"),
|
||||
offset=0,
|
||||
size=5)
|
||||
assert data == b"hello"
|
||||
mock_client.get_object.assert_called_once()
|
||||
call_kwargs = mock_client.get_object.call_args[1]
|
||||
assert "Range" in call_kwargs
|
||||
assert call_kwargs["Range"] == "bytes=0-4"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_put_bytes(s3_accessor):
|
||||
from mirage.core.s3.write import write_bytes
|
||||
|
||||
with patch("mirage.core.s3.write.async_session") as mock_session:
|
||||
mock_client = AsyncMock()
|
||||
mock_ctx = AsyncMock()
|
||||
mock_ctx.__aenter__.return_value = mock_client
|
||||
mock_session.return_value.client.return_value = mock_ctx
|
||||
await write_bytes(
|
||||
s3_accessor,
|
||||
PathSpec(resource_path="data/out.txt",
|
||||
virtual="/data/out.txt",
|
||||
directory="/data/out.txt"), b"hello")
|
||||
mock_client.put_object.assert_called_once()
|
||||
call_kwargs = mock_client.put_object.call_args[1]
|
||||
assert call_kwargs["Body"] == b"hello"
|
||||
assert call_kwargs["Key"] == "data/out.txt"
|
||||
@@ -0,0 +1,98 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import asyncio
|
||||
from io import BytesIO
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from mirage.accessor.s3 import S3Accessor
|
||||
from mirage.resource.s3 import S3Config
|
||||
|
||||
|
||||
def _config():
|
||||
return S3Accessor(
|
||||
S3Config(
|
||||
bucket="test-bucket",
|
||||
region="us-east-1",
|
||||
aws_access_key_id="fake",
|
||||
aws_secret_access_key="fake",
|
||||
))
|
||||
|
||||
|
||||
def _mock_session(data: bytes, chunk_size: int = 8192):
|
||||
mock_client = AsyncMock()
|
||||
body_mock = AsyncMock()
|
||||
body_mock.read = AsyncMock(return_value=data)
|
||||
|
||||
async def _iter_chunks(size=chunk_size):
|
||||
buf = BytesIO(data)
|
||||
while True:
|
||||
chunk = buf.read(size)
|
||||
if not chunk:
|
||||
break
|
||||
yield chunk
|
||||
|
||||
body_mock.iter_chunks = _iter_chunks
|
||||
mock_client.get_object = AsyncMock(return_value={"Body": body_mock})
|
||||
|
||||
mock_session = MagicMock()
|
||||
ctx = AsyncMock()
|
||||
ctx.__aenter__ = AsyncMock(return_value=mock_client)
|
||||
ctx.__aexit__ = AsyncMock(return_value=False)
|
||||
mock_session.client = MagicMock(return_value=ctx)
|
||||
return mock_session
|
||||
|
||||
|
||||
def test_read_stream_returns_async_iterator():
|
||||
from mirage.core.s3.stream import read_stream
|
||||
config = _config()
|
||||
session = _mock_session(b"hello world")
|
||||
|
||||
async def _run():
|
||||
chunks = []
|
||||
async for chunk in read_stream(config, "test.txt"):
|
||||
chunks.append(chunk)
|
||||
return chunks
|
||||
|
||||
with patch("mirage.core.s3.stream.async_session", return_value=session):
|
||||
chunks = asyncio.run(_run())
|
||||
assert b"".join(chunks) == b"hello world"
|
||||
|
||||
|
||||
def test_read_stream_yields_chunks():
|
||||
from mirage.core.s3.stream import read_stream
|
||||
config = _config()
|
||||
session = _mock_session(b"a" * 100, chunk_size=30)
|
||||
|
||||
async def _run():
|
||||
chunks = []
|
||||
async for chunk in read_stream(config, "test.txt", chunk_size=30):
|
||||
chunks.append(chunk)
|
||||
return chunks
|
||||
|
||||
with patch("mirage.core.s3.stream.async_session", return_value=session):
|
||||
chunks = asyncio.run(_run())
|
||||
assert b"".join(chunks) == b"a" * 100
|
||||
assert len(chunks) >= 2
|
||||
|
||||
|
||||
def test_read_bytes_returns_bytes():
|
||||
from mirage.core.s3.read import read_bytes
|
||||
config = _config()
|
||||
session = _mock_session(b"file content here")
|
||||
|
||||
with patch("mirage.core.s3.read.async_session", return_value=session):
|
||||
result = asyncio.run(read_bytes(config, "test.txt"))
|
||||
assert isinstance(result, bytes)
|
||||
assert result == b"file content here"
|
||||
@@ -0,0 +1,64 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.scaleway import ScalewayConfig, ScalewayResource
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_scaleway_regional_endpoint():
|
||||
config = ScalewayConfig(bucket="b",
|
||||
region="fr-par",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == "https://s3.fr-par.scw.cloud"
|
||||
|
||||
|
||||
def test_scaleway_requires_region():
|
||||
with pytest.raises(ValidationError):
|
||||
ScalewayConfig(bucket="b", access_key_id="k", secret_access_key="s")
|
||||
|
||||
|
||||
def test_scaleway_custom_endpoint_override():
|
||||
config = ScalewayConfig(bucket="b",
|
||||
region="nl-ams",
|
||||
endpoint_url="https://custom.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == "https://custom.example.com"
|
||||
|
||||
|
||||
def test_scaleway_to_s3_config():
|
||||
config = ScalewayConfig(bucket="b",
|
||||
region="pl-waw",
|
||||
access_key_id="key",
|
||||
secret_access_key="secret")
|
||||
s3 = config.to_s3_config()
|
||||
assert isinstance(s3, S3Config)
|
||||
assert s3.endpoint_url == "https://s3.pl-waw.scw.cloud"
|
||||
assert reveal_secret(s3.aws_access_key_id) == "key"
|
||||
|
||||
|
||||
def test_scaleway_resource_uses_s3_resource_type():
|
||||
resource = ScalewayResource(
|
||||
ScalewayConfig(bucket="b",
|
||||
region="fr-par",
|
||||
access_key_id="k",
|
||||
secret_access_key="s"))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert isinstance(resource.config, S3Config)
|
||||
@@ -0,0 +1,97 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.seaweedfs import SeaweedFSConfig, SeaweedFSResource
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_seaweedfs_config_defaults():
|
||||
config = SeaweedFSConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="http://localhost:8333",
|
||||
access_key_id="weed",
|
||||
secret_access_key="weed",
|
||||
)
|
||||
assert config.region == "us-east-1"
|
||||
assert config.path_style is True
|
||||
assert config.timeout == 30
|
||||
|
||||
|
||||
def test_seaweedfs_config_immutable():
|
||||
config = SeaweedFSConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="http://localhost:8333",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
)
|
||||
with pytest.raises(ValidationError):
|
||||
config.bucket = "other"
|
||||
|
||||
|
||||
def test_seaweedfs_config_to_s3_config():
|
||||
config = SeaweedFSConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="http://localhost:8333",
|
||||
access_key_id="weed",
|
||||
secret_access_key="weed",
|
||||
proxy="http://localhost:8080",
|
||||
)
|
||||
s3 = config.to_s3_config()
|
||||
assert isinstance(s3, S3Config)
|
||||
assert s3.bucket == "my-bucket"
|
||||
assert s3.endpoint_url == "http://localhost:8333"
|
||||
assert s3.path_style is True
|
||||
assert reveal_secret(s3.aws_access_key_id) == "weed"
|
||||
assert reveal_secret(s3.aws_secret_access_key) == "weed"
|
||||
assert s3.proxy == "http://localhost:8080"
|
||||
|
||||
|
||||
def test_seaweedfs_config_path_style_override():
|
||||
config = SeaweedFSConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="https://seaweedfs.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
path_style=False,
|
||||
)
|
||||
assert config.to_s3_config().path_style is False
|
||||
|
||||
|
||||
def test_seaweedfs_resource_uses_s3_resource_type():
|
||||
resource = SeaweedFSResource(
|
||||
SeaweedFSConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="http://localhost:8333",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert resource.caches_reads is True
|
||||
assert isinstance(resource.config, S3Config)
|
||||
|
||||
|
||||
def test_seaweedfs_resource_preserves_original_config():
|
||||
config = SeaweedFSConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="http://localhost:8333",
|
||||
access_key_id="k",
|
||||
secret_access_key="s",
|
||||
)
|
||||
resource = SeaweedFSResource(config)
|
||||
assert resource.seaweedfs_config is config
|
||||
@@ -0,0 +1,123 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.core.s3._client import _client_kwargs
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.resource.supabase import SupabaseConfig, SupabaseResource
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_supabase_config_defaults():
|
||||
config = SupabaseConfig(
|
||||
bucket="my-bucket",
|
||||
project_ref="project-123",
|
||||
region="us-west-2",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
)
|
||||
assert config.timeout == 30
|
||||
assert config.resolved_endpoint_url() == (
|
||||
"https://project-123.storage.supabase.co/storage/v1/s3")
|
||||
|
||||
|
||||
def test_supabase_config_immutable():
|
||||
config = SupabaseConfig(
|
||||
bucket="my-bucket",
|
||||
project_ref="project-123",
|
||||
region="us-west-2",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
)
|
||||
with pytest.raises(ValidationError):
|
||||
config.bucket = "other-bucket"
|
||||
|
||||
|
||||
def test_supabase_config_to_s3_config():
|
||||
config = SupabaseConfig(
|
||||
bucket="my-bucket",
|
||||
endpoint_url="https://example.supabase.co/storage/v1/s3",
|
||||
region="us-west-2",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
session_token="session-token",
|
||||
proxy="http://localhost:8080",
|
||||
)
|
||||
s3_config = config.to_s3_config()
|
||||
assert isinstance(s3_config, S3Config)
|
||||
assert s3_config.bucket == "my-bucket"
|
||||
assert s3_config.region == "us-west-2"
|
||||
assert (
|
||||
s3_config.endpoint_url == "https://example.supabase.co/storage/v1/s3")
|
||||
assert reveal_secret(s3_config.aws_access_key_id) == "access-key"
|
||||
assert reveal_secret(s3_config.aws_secret_access_key) == "secret-key"
|
||||
assert reveal_secret(s3_config.aws_session_token) == "session-token"
|
||||
assert s3_config.path_style is True
|
||||
assert s3_config.proxy == "http://localhost:8080"
|
||||
|
||||
|
||||
def test_supabase_config_requires_project_ref_or_endpoint():
|
||||
config = SupabaseConfig(
|
||||
bucket="my-bucket",
|
||||
region="us-west-2",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
)
|
||||
with pytest.raises(ValueError):
|
||||
config.resolved_endpoint_url()
|
||||
|
||||
|
||||
def test_supabase_resource_uses_s3_resource_type():
|
||||
resource = SupabaseResource(
|
||||
SupabaseConfig(
|
||||
bucket="my-bucket",
|
||||
project_ref="project-123",
|
||||
region="us-west-2",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert resource.caches_reads is True
|
||||
assert isinstance(resource.config, S3Config)
|
||||
assert resource.config.path_style is True
|
||||
|
||||
|
||||
def test_supabase_resource_preserves_original_config():
|
||||
config = SupabaseConfig(
|
||||
bucket="my-bucket",
|
||||
project_ref="project-123",
|
||||
region="us-west-2",
|
||||
access_key_id="access-key",
|
||||
secret_access_key="secret-key",
|
||||
)
|
||||
resource = SupabaseResource(config)
|
||||
assert resource.supabase_config is config
|
||||
|
||||
|
||||
def test_s3_client_kwargs_support_path_style_and_session_token():
|
||||
config = S3Config(
|
||||
bucket="my-bucket",
|
||||
region="us-west-2",
|
||||
endpoint_url="https://example.com",
|
||||
aws_access_key_id="access-key",
|
||||
aws_secret_access_key="secret-key",
|
||||
aws_session_token="session-token",
|
||||
path_style=True,
|
||||
)
|
||||
kwargs = _client_kwargs(config)
|
||||
assert kwargs["aws_session_token"] == "session-token"
|
||||
assert kwargs["config"].s3 == {"addressing_style": "path"}
|
||||
@@ -0,0 +1,65 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.resource.tencent import TencentConfig, TencentResource
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_tencent_regional_endpoint():
|
||||
config = TencentConfig(bucket="b-1250",
|
||||
region="ap-guangzhou",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == (
|
||||
"https://cos.ap-guangzhou.myqcloud.com")
|
||||
|
||||
|
||||
def test_tencent_requires_region():
|
||||
with pytest.raises(ValidationError):
|
||||
TencentConfig(bucket="b", access_key_id="k", secret_access_key="s")
|
||||
|
||||
|
||||
def test_tencent_custom_endpoint_override():
|
||||
config = TencentConfig(bucket="b",
|
||||
region="ap-beijing",
|
||||
endpoint_url="https://custom.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == "https://custom.example.com"
|
||||
|
||||
|
||||
def test_tencent_to_s3_config():
|
||||
config = TencentConfig(bucket="b-1250",
|
||||
region="ap-singapore",
|
||||
access_key_id="key",
|
||||
secret_access_key="secret")
|
||||
s3 = config.to_s3_config()
|
||||
assert isinstance(s3, S3Config)
|
||||
assert s3.endpoint_url == "https://cos.ap-singapore.myqcloud.com"
|
||||
assert reveal_secret(s3.aws_access_key_id) == "key"
|
||||
|
||||
|
||||
def test_tencent_resource_uses_s3_resource_type():
|
||||
resource = TencentResource(
|
||||
TencentConfig(bucket="b-1250",
|
||||
region="ap-guangzhou",
|
||||
access_key_id="k",
|
||||
secret_access_key="s"))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert isinstance(resource.config, S3Config)
|
||||
@@ -0,0 +1,69 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from mirage.resource.s3 import S3Config
|
||||
from mirage.resource.secrets import reveal_secret
|
||||
from mirage.resource.wasabi import WasabiConfig, WasabiResource
|
||||
from mirage.types import ResourceName
|
||||
|
||||
|
||||
def test_wasabi_default_region_endpoint():
|
||||
config = WasabiConfig(bucket="b", access_key_id="k", secret_access_key="s")
|
||||
assert config.region == "us-east-1"
|
||||
assert config.resolved_endpoint_url() == "https://s3.wasabisys.com"
|
||||
|
||||
|
||||
def test_wasabi_regional_endpoint():
|
||||
config = WasabiConfig(bucket="b",
|
||||
region="us-west-1",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == (
|
||||
"https://s3.us-west-1.wasabisys.com")
|
||||
|
||||
|
||||
def test_wasabi_custom_endpoint_override():
|
||||
config = WasabiConfig(bucket="b",
|
||||
endpoint_url="https://custom.example.com",
|
||||
access_key_id="k",
|
||||
secret_access_key="s")
|
||||
assert config.resolved_endpoint_url() == "https://custom.example.com"
|
||||
|
||||
|
||||
def test_wasabi_config_immutable():
|
||||
config = WasabiConfig(bucket="b", access_key_id="k", secret_access_key="s")
|
||||
with pytest.raises(ValidationError):
|
||||
config.bucket = "other"
|
||||
|
||||
|
||||
def test_wasabi_to_s3_config():
|
||||
config = WasabiConfig(bucket="b",
|
||||
region="us-east-2",
|
||||
access_key_id="key",
|
||||
secret_access_key="secret")
|
||||
s3 = config.to_s3_config()
|
||||
assert isinstance(s3, S3Config)
|
||||
assert s3.endpoint_url == "https://s3.us-east-2.wasabisys.com"
|
||||
assert s3.path_style is False
|
||||
assert reveal_secret(s3.aws_access_key_id) == "key"
|
||||
|
||||
|
||||
def test_wasabi_resource_uses_s3_resource_type():
|
||||
resource = WasabiResource(
|
||||
WasabiConfig(bucket="b", access_key_id="k", secret_access_key="s"))
|
||||
assert resource.name == ResourceName.S3
|
||||
assert isinstance(resource.config, S3Config)
|
||||
Reference in New Issue
Block a user