Files
wehub-resource-sync e0e362d700
SDK Tests / SDK CI (push) Blocked by required conditions
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
SDK Tests / Python SDK Tests (sandbox) (push) Waiting to run
SDK Tests / CLI Quality (push) Waiting to run
SDK Tests / CLI Tests (push) Waiting to run
SDK Tests / Python SDK Quality (code-interpreter) (push) Waiting to run
SDK Tests / Python SDK Quality (sandbox) (push) Waiting to run
SDK Tests / Python SDK Tests (code-interpreter) (push) Waiting to run
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Waiting to run
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Waiting to run
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Waiting to run
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Waiting to run
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Waiting to run
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Waiting to run
SDK Tests / Go SDK Quality And Tests (push) Waiting to run
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:33 +08:00

211 lines
7.9 KiB
Python

# Copyright 2026 Alibaba Group Holding Ltd.
#
# 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.
"""Unit tests for DockerMetadataStore (per-sandbox file-backed)."""
from datetime import datetime, timezone
from pathlib import Path
from opensandbox_server.services.docker.metadata import (
DockerMetadataStore,
_extract_user_labels,
_is_user_label,
)
class TestIsUserLabel:
def test_user_labels_return_true(self):
assert _is_user_label("team")
assert _is_user_label("app.kubernetes.io/name")
assert _is_user_label("example.com/label")
def test_opensandbox_prefix_returns_false(self):
assert not _is_user_label("opensandbox.io/id")
assert not _is_user_label("opensandbox.io/expires-at")
def test_oci_labels_return_false(self):
assert not _is_user_label("org.opencontainers.image.created")
assert not _is_user_label("org.opencontainers.image.version")
def test_docker_labels_return_false(self):
assert not _is_user_label("com.docker.some-label")
assert not _is_user_label("desktop.docker.other")
class TestExtractUserLabels:
def test_filters_non_user_labels(self):
labels = {
"team": "infra",
"opensandbox.io/id": "sbx-001",
"org.opencontainers.image.created": "2026-01-01",
"com.docker.foo": "bar",
}
result = _extract_user_labels(labels)
assert result == {"team": "infra"}
def test_empty_returns_empty(self):
assert _extract_user_labels({}) == {}
class TestDockerMetadataStore:
@staticmethod
def _make_store(tmp_path: Path):
return DockerMetadataStore(root=tmp_path / "metadata")
# -- get (no overrides) --------------------------------------------------
def test_get_no_overrides_returns_user_labels(self, tmp_path: Path):
store = self._make_store(tmp_path)
labels = {
"team": "infra",
"opensandbox.io/id": "sbx-001",
"org.opencontainers.image.created": "2026-01-01",
}
result = store.get("sbx-001", labels)
assert result == {"team": "infra"}
def test_get_no_user_labels_returns_none(self, tmp_path: Path):
store = self._make_store(tmp_path)
result = store.get("sbx-001", {"opensandbox.io/id": "sbx-001"})
assert result is None
# -- patch ---------------------------------------------------------------
def test_patch_adds_keys(self, tmp_path: Path):
store = self._make_store(tmp_path)
labels = {"team": "infra"}
store.patch("sbx-001", labels, {"project": "new"})
result = store.get("sbx-001", labels)
assert result == {"team": "infra", "project": "new"}
def test_patch_deletes_keys(self, tmp_path: Path):
store = self._make_store(tmp_path)
labels = {"team": "infra", "project": "old"}
store.patch("sbx-001", labels, {"project": None})
result = store.get("sbx-001", labels)
assert result == {"team": "infra"}
def test_patch_replaces_keys(self, tmp_path: Path):
store = self._make_store(tmp_path)
labels = {"team": "old-team"}
store.patch("sbx-001", labels, {"team": "new-team"})
result = store.get("sbx-001", labels)
assert result == {"team": "new-team"}
def test_patch_persists_across_instances(self, tmp_path: Path):
root = tmp_path / "metadata"
store1 = DockerMetadataStore(root=root)
store1.patch("sbx-001", {"hello": "world"}, {"team": "platform"})
store2 = DockerMetadataStore(root=root)
result = store2.get("sbx-001", {"hello": "world"})
assert result == {"hello": "world", "team": "platform"}
def test_patch_empty_noop(self, tmp_path: Path):
store = self._make_store(tmp_path)
labels = {"team": "infra"}
store.patch("sbx-001", labels, {})
result = store.get("sbx-001", labels)
assert result == {"team": "infra"}
def test_patch_from_empty_labels(self, tmp_path: Path):
store = self._make_store(tmp_path)
labels: dict = {}
store.patch("sbx-001", labels, {"new": "value"})
result = store.get("sbx-001", labels)
assert result == {"new": "value"}
def test_patch_delete_nonexistent_silent(self, tmp_path: Path):
store = self._make_store(tmp_path)
labels = {"team": "infra"}
store.patch("sbx-001", labels, {"nonexistent": None})
result = store.get("sbx-001", labels)
assert result == {"team": "infra"}
def test_patch_mixed_add_and_delete(self, tmp_path: Path):
store = self._make_store(tmp_path)
labels = {"team": "infra", "env": "staging", "old": "remove"}
store.patch("sbx-001", labels, {"team": "platform", "env": None, "project": "new"})
result = store.get("sbx-001", labels)
assert result == {"team": "platform", "old": "remove", "project": "new"}
def test_patch_ignores_system_labels(self, tmp_path: Path):
store = self._make_store(tmp_path)
labels = {
"hello": "world",
"opensandbox.io/id": "sbx-001",
"org.opencontainers.image.version": "26.04",
}
store.patch("sbx-001", labels, {"team": "platform"})
result = store.get("sbx-001", labels)
assert result == {"hello": "world", "team": "platform"}
# -- delete --------------------------------------------------------------
def test_delete_removes_file(self, tmp_path: Path):
store = self._make_store(tmp_path)
labels = {"team": "infra"}
store.patch("sbx-001", labels, {"project": "new"})
f = tmp_path / "metadata" / "sbx-001.json"
assert f.exists()
store.delete("sbx-001")
assert not f.exists()
# Falls back to container labels
result = store.get("sbx-001", labels)
assert result == {"team": "infra"}
def test_delete_nonexistent_silent(self, tmp_path: Path):
store = self._make_store(tmp_path)
store.delete("nonexistent") # Should not raise
def test_set_get_expiration_override(self, tmp_path: Path):
store = self._make_store(tmp_path)
expires_at = datetime(2030, 1, 1, tzinfo=timezone.utc)
store.set_expiration("sbx-001", expires_at)
assert store.get_expiration("sbx-001") == expires_at.isoformat()
def test_delete_removes_expiration_override_file(self, tmp_path: Path):
store = self._make_store(tmp_path)
expires_at = datetime(2030, 1, 1, tzinfo=timezone.utc)
store.set_expiration("sbx-001", expires_at)
expiration_file = tmp_path / "metadata" / "_expiration" / "sbx-001.json"
assert expiration_file.exists()
store.delete("sbx-001")
assert not expiration_file.exists()
assert store.get_expiration("sbx-001") is None
# -- crash recovery ------------------------------------------------------
def test_tmp_file_not_read_on_reload(self, tmp_path: Path):
"""Tmp files written during atomic save must not be picked up by get()."""
root = tmp_path / "metadata"
store1 = DockerMetadataStore(root=root)
store1.patch("sbx-001", {"hello": "world"}, {"team": "platform"})
# Simulate a leftover .tmp file
tmp_file = root / "sbx-001.tmp"
tmp_file.write_text('{"bad": "data"}')
store2 = DockerMetadataStore(root=root)
result = store2.get("sbx-001", {"hello": "world"})
assert result == {"hello": "world", "team": "platform"}