unitysvc-core 0.2.22__tar.gz → 0.2.24__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unitysvc_core-0.2.22/src/unitysvc_core.egg-info → unitysvc_core-0.2.24}/PKG-INFO +1 -1
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/pyproject.toml +1 -1
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/__init__.py +2 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/base.py +11 -6
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/service_group_data.py +4 -3
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/schema/service_group_v1.json +6 -5
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/utils.py +32 -1
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/validator.py +5 -3
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24/src/unitysvc_core.egg-info}/PKG-INFO +1 -1
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core.egg-info/SOURCES.txt +1 -0
- unitysvc_core-0.2.24/tests/test_hidden_path_skip.py +105 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/tests/test_service_group.py +27 -4
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/LICENSE +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/README.md +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/setup.cfg +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/__init__.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/documents.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/listing_data.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/listing_v1.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/offering_data.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/offering_v1.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/pricing.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/promotion_data.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/provider_data.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/provider_v1.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/service.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/validators.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/py.typed +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/schema/base.json +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/schema/listing_v1.json +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/schema/offering_v1.json +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/schema/promotion_v1.json +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/schema/provider_v1.json +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core.egg-info/dependency_links.txt +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core.egg-info/requires.txt +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core.egg-info/top_level.txt +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/tests/test_access_interface_routing.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/tests/test_pricing.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/tests/test_provider_account_rate_limits.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/tests/test_service_status.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/tests/test_utils.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/tests/test_validator.py +0 -0
- {unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/tests/test_validators.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "unitysvc-core"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.24"
|
|
8
8
|
description = "Shared data models and validation helpers for UnitySVC packages"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Bo Peng", email = "bo.peng@unitysvc.com" }]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from .base import (
|
|
2
|
+
ROUTABLE_GROUP_TYPES,
|
|
2
3
|
AccessMethodEnum,
|
|
3
4
|
AuthMethodEnum,
|
|
4
5
|
ContentFilterEnum,
|
|
@@ -131,6 +132,7 @@ __all__ = [
|
|
|
131
132
|
"QuotaResetCycleEnum",
|
|
132
133
|
"RateLimitUnitEnum",
|
|
133
134
|
"RequestTransformEnum",
|
|
135
|
+
"ROUTABLE_GROUP_TYPES",
|
|
134
136
|
"SellerTypeEnum",
|
|
135
137
|
"ServiceGroupStatusEnum",
|
|
136
138
|
"ServiceTypeEnum",
|
|
@@ -136,16 +136,21 @@ class GroupOwnerTypeEnum(StrEnum):
|
|
|
136
136
|
|
|
137
137
|
|
|
138
138
|
class GroupTypeEnum(StrEnum):
|
|
139
|
-
"""Type of service group.
|
|
139
|
+
"""Type of service group. Derived from members, not authored.
|
|
140
140
|
|
|
141
|
-
- ``
|
|
142
|
-
- ``
|
|
143
|
-
- ``
|
|
141
|
+
- ``category``: Parent group for browsing/organization only.
|
|
142
|
+
- ``collection``: Services-bearing group that is not routable.
|
|
143
|
+
- ``open``: Routable group where keyless requests can fan safely.
|
|
144
|
+
- ``keyed``: Routable group where callers must provide a routing key.
|
|
144
145
|
"""
|
|
145
146
|
|
|
146
|
-
regular = "regular"
|
|
147
147
|
category = "category"
|
|
148
|
-
|
|
148
|
+
collection = "collection"
|
|
149
|
+
open = "open"
|
|
150
|
+
keyed = "keyed"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
ROUTABLE_GROUP_TYPES = frozenset({GroupTypeEnum.open, GroupTypeEnum.keyed})
|
|
149
154
|
|
|
150
155
|
|
|
151
156
|
class SellerTypeEnum(StrEnum):
|
{unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/models/service_group_data.py
RENAMED
|
@@ -84,10 +84,11 @@ class ServiceGroupData(BaseModel):
|
|
|
84
84
|
)
|
|
85
85
|
|
|
86
86
|
group_type: GroupTypeEnum = Field(
|
|
87
|
-
default=GroupTypeEnum.
|
|
87
|
+
default=GroupTypeEnum.category,
|
|
88
88
|
description=(
|
|
89
|
-
"
|
|
90
|
-
"
|
|
89
|
+
"Backend-derived group type: category (browse-only parent), "
|
|
90
|
+
"collection (not routable), open (routable keyless fanout), "
|
|
91
|
+
"or keyed (routable by key)."
|
|
91
92
|
),
|
|
92
93
|
)
|
|
93
94
|
|
{unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core/schema/service_group_v1.json
RENAMED
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
"type": "string"
|
|
12
12
|
},
|
|
13
13
|
"GroupTypeEnum": {
|
|
14
|
-
"description": "Type of service group.\n\n- ``
|
|
14
|
+
"description": "Type of service group. Derived from members, not authored.\n\n- ``category``: Parent group for browsing/organization only.\n- ``collection``: Services-bearing group that is not routable.\n- ``open``: Routable group where keyless requests can fan safely.\n- ``keyed``: Routable group where callers must provide a routing key.",
|
|
15
15
|
"enum": [
|
|
16
|
-
"regular",
|
|
17
16
|
"category",
|
|
18
|
-
"
|
|
17
|
+
"collection",
|
|
18
|
+
"open",
|
|
19
|
+
"keyed"
|
|
19
20
|
],
|
|
20
21
|
"title": "GroupTypeEnum",
|
|
21
22
|
"type": "string"
|
|
@@ -69,8 +70,8 @@
|
|
|
69
70
|
},
|
|
70
71
|
"group_type": {
|
|
71
72
|
"$ref": "#/$defs/GroupTypeEnum",
|
|
72
|
-
"default": "
|
|
73
|
-
"description": "
|
|
73
|
+
"default": "category",
|
|
74
|
+
"description": "Backend-derived group type: category (browse-only parent), collection (not routable), open (routable keyless fanout), or keyed (routable by key)."
|
|
74
75
|
},
|
|
75
76
|
"membership_rules": {
|
|
76
77
|
"anyOf": [
|
|
@@ -357,10 +357,41 @@ def write_data_file(file_path: Path, data: dict[str, Any], format: str) -> None:
|
|
|
357
357
|
raise ValueError(f"Unsupported format: {format}")
|
|
358
358
|
|
|
359
359
|
|
|
360
|
+
def is_hidden_path(path: Path, root: Path | str) -> bool:
|
|
361
|
+
"""True when *path* lies inside a dot-directory (or is a dot-file) under *root*.
|
|
362
|
+
|
|
363
|
+
Hidden directories are tooling territory, never catalog data — and the
|
|
364
|
+
expensive case is a **second copy of the catalog parked inside it**: a git
|
|
365
|
+
worktree at ``.claude/worktrees/<branch>/``, a ``.venv`` holding vendored
|
|
366
|
+
fixtures. Such a tree carries duplicate spec files whose listings have the
|
|
367
|
+
*same* ``listing.name``, so a walk that descends into one discovers every
|
|
368
|
+
service twice and a consumer that uploads what it finds races the stale
|
|
369
|
+
copy onto the real service's backend id.
|
|
370
|
+
|
|
371
|
+
The check is on the path **relative to the walk root**, so a catalog that
|
|
372
|
+
legitimately lives under a hidden directory (``~/.cache/repo/specs/…``) is
|
|
373
|
+
still discovered in full. Paths outside *root* are kept: the caller named
|
|
374
|
+
them explicitly rather than finding them by walking.
|
|
375
|
+
"""
|
|
376
|
+
path = Path(path)
|
|
377
|
+
try:
|
|
378
|
+
rel = path.relative_to(root)
|
|
379
|
+
except ValueError:
|
|
380
|
+
try:
|
|
381
|
+
rel = path.resolve().relative_to(Path(root).resolve())
|
|
382
|
+
except ValueError:
|
|
383
|
+
return False
|
|
384
|
+
return any(part.startswith(".") for part in rel.parts)
|
|
385
|
+
|
|
386
|
+
|
|
360
387
|
def find_data_files(data_dir: Path, extensions: tuple[str, ...] | None = None) -> list[Path]:
|
|
361
388
|
"""
|
|
362
389
|
Find all data files in a directory with specified extensions.
|
|
363
390
|
|
|
391
|
+
Hidden trees are skipped — see :func:`is_hidden_path`. This is the walker
|
|
392
|
+
under :func:`find_files_by_pattern` and :func:`find_files_by_schema`, so
|
|
393
|
+
every schema-based discovery inherits the skip.
|
|
394
|
+
|
|
364
395
|
Args:
|
|
365
396
|
data_dir: Directory to search
|
|
366
397
|
extensions: Tuple of extensions to search for (default: ("json", "toml"))
|
|
@@ -373,7 +404,7 @@ def find_data_files(data_dir: Path, extensions: tuple[str, ...] | None = None) -
|
|
|
373
404
|
|
|
374
405
|
data_files: list[Path] = []
|
|
375
406
|
for ext in extensions:
|
|
376
|
-
data_files.extend(data_dir.rglob(f"*.{ext}"))
|
|
407
|
+
data_files.extend(p for p in data_dir.rglob(f"*.{ext}") if not is_hidden_path(p, data_dir))
|
|
377
408
|
|
|
378
409
|
return data_files
|
|
379
410
|
|
|
@@ -10,7 +10,7 @@ from urllib.parse import urlparse
|
|
|
10
10
|
from jinja2 import Environment, StrictUndefined, TemplateSyntaxError, UndefinedError
|
|
11
11
|
from jsonschema.validators import Draft7Validator
|
|
12
12
|
|
|
13
|
-
from .utils import SCHEMA_TO_STEM, load_data_file, schema_for_path
|
|
13
|
+
from .utils import SCHEMA_TO_STEM, is_hidden_path, load_data_file, schema_for_path
|
|
14
14
|
|
|
15
15
|
# svcpass api_key dispositions (#1198, #1786). On an upstream_access_config
|
|
16
16
|
# interface these literal values are not credentials: "" / "__strip__" strip
|
|
@@ -1059,8 +1059,10 @@ class DataValidator:
|
|
|
1059
1059
|
|
|
1060
1060
|
# Find all data and MD files recursively, skipping hidden directories
|
|
1061
1061
|
for file_path in self.data_dir.rglob("*"):
|
|
1062
|
-
# Skip hidden directories (those starting with .)
|
|
1063
|
-
|
|
1062
|
+
# Skip hidden directories (those starting with .) — relative to the
|
|
1063
|
+
# data dir, so a catalog that itself lives under one (~/.cache/repo)
|
|
1064
|
+
# still validates instead of silently reporting zero files.
|
|
1065
|
+
if is_hidden_path(file_path, self.data_dir):
|
|
1064
1066
|
continue
|
|
1065
1067
|
|
|
1066
1068
|
# Skip schema directory, docs directory, and pyproject.toml (not data files)
|
|
@@ -31,6 +31,7 @@ src/unitysvc_core/schema/promotion_v1.json
|
|
|
31
31
|
src/unitysvc_core/schema/provider_v1.json
|
|
32
32
|
src/unitysvc_core/schema/service_group_v1.json
|
|
33
33
|
tests/test_access_interface_routing.py
|
|
34
|
+
tests/test_hidden_path_skip.py
|
|
34
35
|
tests/test_pricing.py
|
|
35
36
|
tests/test_provider_account_rate_limits.py
|
|
36
37
|
tests/test_service_group.py
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Discovery walks must ignore hidden dot-directories.
|
|
2
|
+
|
|
3
|
+
Tooling routinely parks a *second copy of the catalog* inside the catalog: a
|
|
4
|
+
git worktree under ``.claude/worktrees/<branch>/``, a ``.venv`` holding
|
|
5
|
+
vendored fixtures, an editor cache. Those copies carry the same spec files with
|
|
6
|
+
the same ``listing.name``, so a walk that descends into them yields every
|
|
7
|
+
service twice — and a consumer that uploads what it discovers races the stale
|
|
8
|
+
copy onto the real service's backend id.
|
|
9
|
+
|
|
10
|
+
The skip is computed **relative to the walk root**, which is the other half of
|
|
11
|
+
the contract: a catalog that legitimately lives under a hidden directory
|
|
12
|
+
(``~/.cache/repo/specs/…``) must still be discovered in full.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
import pytest
|
|
21
|
+
|
|
22
|
+
from unitysvc_core.utils import find_data_files, find_files_by_pattern, is_hidden_path
|
|
23
|
+
from unitysvc_core.validator import DataValidator
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def schema_dir() -> Path:
|
|
28
|
+
return Path(__file__).parent.parent / "src" / "unitysvc_core" / "schema"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _write_listing(folder: Path, name: str) -> Path:
|
|
32
|
+
folder.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
listing = folder / "listing.json"
|
|
34
|
+
listing.write_text(json.dumps({"name": name}))
|
|
35
|
+
return listing
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# --- the predicate ---------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_is_hidden_path_detects_a_dot_directory_below_the_root(tmp_path: Path) -> None:
|
|
42
|
+
assert is_hidden_path(tmp_path / ".claude" / "worktrees" / "wt" / "listing.json", tmp_path)
|
|
43
|
+
assert is_hidden_path(tmp_path / "specs" / ".git" / "listing.json", tmp_path)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_is_hidden_path_ignores_dots_above_the_root(tmp_path: Path) -> None:
|
|
47
|
+
"""A repo cloned into ``.cache/`` is a normal catalog, not a hidden one."""
|
|
48
|
+
root = tmp_path / ".cache" / "repo"
|
|
49
|
+
assert not is_hidden_path(root / "specs" / "svc" / "listing.json", root)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_is_hidden_path_keeps_paths_outside_the_root(tmp_path: Path) -> None:
|
|
53
|
+
"""Unrelatable paths were named explicitly, not found by walking."""
|
|
54
|
+
assert not is_hidden_path(Path("/elsewhere/listing.json"), tmp_path)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# --- the walks ------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_find_data_files_skips_hidden_directories(tmp_path: Path) -> None:
|
|
61
|
+
real = _write_listing(tmp_path / "specs" / "svc", "labs/svc")
|
|
62
|
+
worktree = _write_listing(tmp_path / ".claude" / "worktrees" / "wt" / "specs" / "svc", "labs/svc")
|
|
63
|
+
|
|
64
|
+
found = find_data_files(tmp_path)
|
|
65
|
+
|
|
66
|
+
assert real in found
|
|
67
|
+
assert worktree not in found
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_find_data_files_keeps_a_catalog_under_a_hidden_root(tmp_path: Path) -> None:
|
|
71
|
+
root = tmp_path / ".cache" / "repo"
|
|
72
|
+
real = _write_listing(root / "specs" / "svc", "labs/svc")
|
|
73
|
+
|
|
74
|
+
assert find_data_files(root) == [real]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_find_files_by_pattern_skips_hidden_directories(tmp_path: Path) -> None:
|
|
78
|
+
"""The seller/admin upload path: one service, discovered once."""
|
|
79
|
+
real = _write_listing(tmp_path / "specs" / "svc", "labs/svc")
|
|
80
|
+
worktree = _write_listing(tmp_path / ".claude" / "worktrees" / "wt" / "specs" / "svc", "labs/svc")
|
|
81
|
+
|
|
82
|
+
found = [p for p, _fmt, _data in find_files_by_pattern(tmp_path, "listing_v1")]
|
|
83
|
+
|
|
84
|
+
assert found == [real]
|
|
85
|
+
assert worktree not in found
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_validate_all_reaches_a_catalog_under_a_hidden_root(tmp_path: Path, schema_dir: Path) -> None:
|
|
89
|
+
"""``validate_all`` skipped dot-parts of the *absolute* path, so a catalog
|
|
90
|
+
under ``~/.cache/`` validated zero files and reported success."""
|
|
91
|
+
root = tmp_path / ".cache" / "repo"
|
|
92
|
+
_write_listing(root / "specs" / "svc", "labs/svc")
|
|
93
|
+
|
|
94
|
+
results = DataValidator(root, schema_dir).validate_all()
|
|
95
|
+
|
|
96
|
+
assert [Path(k).as_posix() for k in results] == ["specs/svc/listing.json"]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_validate_all_skips_hidden_directories(tmp_path: Path, schema_dir: Path) -> None:
|
|
100
|
+
_write_listing(tmp_path / "specs" / "svc", "labs/svc")
|
|
101
|
+
_write_listing(tmp_path / ".claude" / "worktrees" / "wt" / "specs" / "svc", "labs/svc")
|
|
102
|
+
|
|
103
|
+
results = DataValidator(tmp_path, schema_dir).validate_all()
|
|
104
|
+
|
|
105
|
+
assert [Path(k).as_posix() for k in results] == ["specs/svc/listing.json"]
|
|
@@ -46,22 +46,44 @@ class TestServiceGroupDataAdminFields:
|
|
|
46
46
|
g = ServiceGroupData(name="ok", display_name="X")
|
|
47
47
|
assert g.owner_type is GroupOwnerTypeEnum.platform
|
|
48
48
|
assert g.owner_id is None
|
|
49
|
-
assert g.group_type is GroupTypeEnum.
|
|
49
|
+
assert g.group_type is GroupTypeEnum.category
|
|
50
50
|
assert g.access_interface_data_template is None
|
|
51
51
|
|
|
52
|
+
@pytest.mark.parametrize(
|
|
53
|
+
"group_type",
|
|
54
|
+
[
|
|
55
|
+
GroupTypeEnum.category,
|
|
56
|
+
GroupTypeEnum.collection,
|
|
57
|
+
GroupTypeEnum.open,
|
|
58
|
+
GroupTypeEnum.keyed,
|
|
59
|
+
],
|
|
60
|
+
)
|
|
61
|
+
def test_backend_group_types_set(self, group_type: GroupTypeEnum):
|
|
62
|
+
g = ServiceGroupData(
|
|
63
|
+
name="ok",
|
|
64
|
+
display_name="X",
|
|
65
|
+
group_type=group_type,
|
|
66
|
+
)
|
|
67
|
+
assert g.group_type is group_type
|
|
68
|
+
|
|
52
69
|
def test_admin_fields_set(self):
|
|
53
70
|
g = ServiceGroupData(
|
|
54
71
|
name="ok",
|
|
55
72
|
display_name="X",
|
|
56
73
|
owner_type=GroupOwnerTypeEnum.seller,
|
|
57
74
|
owner_id="00000000-0000-0000-0000-000000000001",
|
|
58
|
-
group_type=GroupTypeEnum.
|
|
75
|
+
group_type=GroupTypeEnum.open,
|
|
59
76
|
access_interface_data_template='{"base_url": "https://x"}',
|
|
60
77
|
)
|
|
61
78
|
assert g.owner_type is GroupOwnerTypeEnum.seller
|
|
62
|
-
assert g.group_type is GroupTypeEnum.
|
|
79
|
+
assert g.group_type is GroupTypeEnum.open
|
|
63
80
|
assert g.access_interface_data_template == '{"base_url": "https://x"}'
|
|
64
81
|
|
|
82
|
+
@pytest.mark.parametrize("legacy_type", ["regular", "misc"])
|
|
83
|
+
def test_legacy_group_types_rejected(self, legacy_type: str):
|
|
84
|
+
with pytest.raises(ValidationError):
|
|
85
|
+
ServiceGroupData(name="ok", display_name="X", group_type=legacy_type)
|
|
86
|
+
|
|
65
87
|
|
|
66
88
|
class TestServiceGroupV1FileModel:
|
|
67
89
|
"""``extra='forbid'`` keeps file-level validation strict."""
|
|
@@ -78,10 +100,11 @@ class TestServiceGroupV1FileModel:
|
|
|
78
100
|
name="my-group",
|
|
79
101
|
display_name="X",
|
|
80
102
|
owner_type="platform",
|
|
81
|
-
group_type="
|
|
103
|
+
group_type="keyed",
|
|
82
104
|
status="active",
|
|
83
105
|
)
|
|
84
106
|
assert g.status is ServiceGroupStatusEnum.active
|
|
107
|
+
assert g.group_type is GroupTypeEnum.keyed
|
|
85
108
|
|
|
86
109
|
|
|
87
110
|
class TestValidateServiceGroupDict:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unitysvc_core-0.2.22 → unitysvc_core-0.2.24}/src/unitysvc_core.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|