Skip to content

Commit ba713b1

Browse files
committed
test: add comprehensive tests for gts-spec v0.7 changes
Add 11 tests covering all three breaking changes: Issue #25 tests (2): - test_issue_25_schema_validation: Verify $schema field validation - test_store_validation_issue_25: Verify GtsStore rejects GTS IDs in $schema Issue #31 tests (1): - test_issue_31_id_extraction_with_gts_prefix: Verify gts:// prefix normalization Issue #32 tests (8): - test_issue_32_ref_extraction_with_gts_prefix: Parse gts:// in $ref - test_store_ref_resolution_issue_32: RefResolver handles gts:// scheme - test_validate_schema_refs_rejects_bare_gts_id: Reject bare GTS IDs - test_validate_schema_refs_rejects_external_http_ref: Reject HTTP refs - test_validate_schema_refs_accepts_local_json_pointer: Accept #/... refs - test_validate_schema_refs_accepts_gts_uri: Accept gts:// refs - test_validate_schema_refs_rejects_malformed_gts_id: Reject invalid IDs - test_validate_schema_refs_nested_invalid_ref: Verify error paths All tests pass successfully (11/11). Signed-off-by: Dmitrii Efremov <kaidendev@icloud.com>
1 parent 820b94c commit ba713b1

1 file changed

Lines changed: 301 additions & 0 deletions

File tree

test_changes.py

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
import sys
2+
import os
3+
import unittest
4+
from typing import Dict, Any
5+
6+
# Add src to path
7+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "gts/src")))
8+
9+
from gts.entities import GtsConfig, DEFAULT_GTS_CONFIG, GtsEntity, GtsID
10+
from gts.store import GtsStore, GtsReader
11+
12+
class MockReader(GtsReader):
13+
def __init__(self, entities):
14+
self.entities = entities
15+
def __iter__(self):
16+
for e in self.entities:
17+
yield e
18+
def read_by_id(self, entity_id):
19+
for e in self.entities:
20+
if e.gts_id and e.gts_id.id == entity_id:
21+
return e
22+
return None
23+
def reset(self):
24+
pass
25+
26+
class TestProtocolUpdates(unittest.TestCase):
27+
28+
def test_issue_25_schema_validation(self):
29+
"""Issue #25: strict $schema usage."""
30+
# 1. Verify defaults
31+
self.assertNotIn("$schema", DEFAULT_GTS_CONFIG.schema_id_fields)
32+
33+
# 2. Verify _is_json_schema_entity logic
34+
# Valid JSON Schema
35+
valid_schema = {"$schema": "http://json-schema.org/draft-07/schema#", "gtsId": "gts.vendor.pkg.ns.type.v1~"}
36+
e = GtsEntity(content=valid_schema, cfg=DEFAULT_GTS_CONFIG)
37+
self.assertTrue(e.is_schema, "Should be detected as schema")
38+
39+
# Invalid: GTS ID in $schema
40+
invalid_schema = {"$schema": "gts.vendor.meta.ns.type.v1~", "gtsId": "gts.vendor.pkg.ns.type.v1~"}
41+
e = GtsEntity(content=invalid_schema, cfg=DEFAULT_GTS_CONFIG)
42+
self.assertFalse(e.is_schema, "Should NOT be detected as schema if $schema is GTS ID")
43+
44+
# Invalid: gts:// URI in $schema
45+
invalid_schema_uri = {"$schema": "gts://gts.vendor.meta.ns.type.v1~", "gtsId": "gts.vendor.pkg.ns.type.v1~"}
46+
e = GtsEntity(content=invalid_schema_uri, cfg=DEFAULT_GTS_CONFIG)
47+
self.assertFalse(e.is_schema, "Should NOT be detected as schema if $schema is gts://")
48+
49+
def test_issue_31_id_extraction_with_gts_prefix(self):
50+
"""Issue #31: Handle gts:// in ID fields."""
51+
# Case 1: $id with gts://
52+
content = {
53+
"$schema": "http://json-schema.org/draft-07/schema#",
54+
"$id": "gts://gts.vendor.test.ns.type.v1~",
55+
"type": "object"
56+
}
57+
e = GtsEntity(content=content, cfg=DEFAULT_GTS_CONFIG)
58+
self.assertTrue(e.is_schema)
59+
self.assertIsNotNone(e.gts_id)
60+
self.assertEqual(e.gts_id.id, "gts.vendor.test.ns.type.v1~")
61+
62+
# Case 2: gtsId with gts://
63+
content2 = {
64+
"gtsId": "gts://gts.vendor.data.ns.type.v1~",
65+
"foo": "bar"
66+
}
67+
e2 = GtsEntity(content=content2, cfg=DEFAULT_GTS_CONFIG)
68+
self.assertIsNotNone(e2.gts_id)
69+
self.assertEqual(e2.gts_id.id, "gts.vendor.data.ns.type.v1~")
70+
71+
def test_issue_32_ref_extraction_with_gts_prefix(self):
72+
"""Issue #32: Handle gts:// in $ref."""
73+
content = {
74+
"$schema": "http://json-schema.org/draft-07/schema#",
75+
"$id": "gts.vendor.parent.ns.type.v1~",
76+
"properties": {
77+
"child": {
78+
"$ref": "gts://gts.vendor.child.ns.type.v1~"
79+
}
80+
}
81+
}
82+
e = GtsEntity(content=content, cfg=DEFAULT_GTS_CONFIG)
83+
84+
# Check extracted refs
85+
refs = [r['id'] for r in e.schemaRefs]
86+
self.assertIn("gts.vendor.child.ns.type.v1~", refs, "Should strip gts:// from $ref")
87+
88+
def test_store_validation_issue_25(self):
89+
"""Verify Store.validate_schema enforces Issue #25."""
90+
store = GtsStore(None)
91+
92+
# Create a schema entity with invalid $schema
93+
# Note: We manually set is_schema=True because auto-detect might fail (as tested above)
94+
# But for the store validation to even run, we need to register it.
95+
bad_schema_id = "gts.vendor.bad.ns.type.v1~"
96+
bad_content = {
97+
"$schema": "gts.vendor.meta.ns.type.v1~",
98+
"$id": bad_schema_id,
99+
"type": "object"
100+
}
101+
# Force it into store
102+
entity = GtsEntity(content=bad_content, gts_id=GtsID(bad_schema_id), is_schema=True)
103+
store.register(entity)
104+
105+
# Validate
106+
with self.assertRaises(ValueError) as cm:
107+
store.validate_schema(bad_schema_id)
108+
self.assertIn("Invalid $schema URL", str(cm.exception))
109+
110+
def test_store_ref_resolution_issue_32(self):
111+
"""Verify RefResolver handles gts:// scheme."""
112+
# 1. Register a target schema
113+
target_id = "gts.vendor.target.ns.type.v1~"
114+
target_content = {
115+
"$schema": "http://json-schema.org/draft-07/schema#",
116+
"$id": target_id,
117+
"type": "string"
118+
}
119+
target_entity = GtsEntity(content=target_content, cfg=DEFAULT_GTS_CONFIG)
120+
121+
# 2. Register a source schema referring to it via gts://
122+
source_id = "gts.vendor.source.ns.type.v1~"
123+
source_content = {
124+
"$schema": "http://json-schema.org/draft-07/schema#",
125+
"$id": source_id,
126+
"type": "object",
127+
"properties": {
128+
"prop": { "$ref": f"gts://{target_id}" }
129+
}
130+
}
131+
source_entity = GtsEntity(content=source_content, cfg=DEFAULT_GTS_CONFIG)
132+
133+
store = GtsStore(MockReader([target_entity, source_entity]))
134+
135+
# 3. Create an instance to validate
136+
instance_id = "gts.vendor.source.ns.type.v1~vendor.pkg.ns.type.v1"
137+
instance_content = {
138+
"gtsId": instance_id,
139+
"prop": "valid string"
140+
}
141+
instance_entity = GtsEntity(content=instance_content, schemaId=source_id, gts_id=GtsID(instance_id))
142+
store.register(instance_entity)
143+
144+
# 4. Validate
145+
try:
146+
store.validate_instance(instance_id)
147+
except Exception as e:
148+
self.fail(f"Validation failed with gts:// ref: {e}")
149+
150+
def test_validate_schema_refs_rejects_bare_gts_id(self):
151+
"""Issue #32: Bare GTS IDs in $ref must be rejected."""
152+
store = GtsStore(None)
153+
154+
schema_id = "gts.vendor.bad.ns.type.v1~"
155+
content = {
156+
"$schema": "http://json-schema.org/draft-07/schema#",
157+
"$id": schema_id,
158+
"properties": {
159+
"child": {
160+
"$ref": "gts.vendor.other.ns.type.v1~"
161+
}
162+
}
163+
}
164+
entity = GtsEntity(content=content, gts_id=GtsID(schema_id), is_schema=True)
165+
store.register(entity)
166+
167+
with self.assertRaises(ValueError) as cm:
168+
store.validate_schema(schema_id)
169+
self.assertIn("must be a local ref", str(cm.exception))
170+
self.assertIn("or a GTS URI", str(cm.exception))
171+
172+
def test_validate_schema_refs_rejects_external_http_ref(self):
173+
"""Issue #32: External HTTP refs must be rejected."""
174+
store = GtsStore(None)
175+
176+
schema_id = "gts.vendor.bad.ns.type.v1~"
177+
content = {
178+
"$schema": "http://json-schema.org/draft-07/schema#",
179+
"$id": schema_id,
180+
"properties": {
181+
"child": {
182+
"$ref": "http://example.com/schema.json"
183+
}
184+
}
185+
}
186+
entity = GtsEntity(content=content, gts_id=GtsID(schema_id), is_schema=True)
187+
store.register(entity)
188+
189+
with self.assertRaises(ValueError) as cm:
190+
store.validate_schema(schema_id)
191+
self.assertIn("must be a local ref", str(cm.exception))
192+
193+
def test_validate_schema_refs_accepts_local_json_pointer(self):
194+
"""Issue #32: Local JSON Pointer refs should be accepted."""
195+
store = GtsStore(None)
196+
197+
schema_id = "gts.vendor.good.ns.type.v1~"
198+
content = {
199+
"$schema": "http://json-schema.org/draft-07/schema#",
200+
"$id": schema_id,
201+
"definitions": {
202+
"Base": {"type": "string"}
203+
},
204+
"properties": {
205+
"child": {
206+
"$ref": "#/definitions/Base"
207+
}
208+
}
209+
}
210+
entity = GtsEntity(content=content, gts_id=GtsID(schema_id), is_schema=True)
211+
store.register(entity)
212+
213+
try:
214+
store.validate_schema(schema_id)
215+
except ValueError as e:
216+
if "$ref" in str(e):
217+
self.fail(f"Local ref should be accepted: {e}")
218+
219+
def test_validate_schema_refs_accepts_gts_uri(self):
220+
"""Issue #32: GTS URIs with gts:// prefix should be accepted."""
221+
target_id = "gts.vendor.target.ns.type.v1~"
222+
target_content = {
223+
"$schema": "http://json-schema.org/draft-07/schema#",
224+
"$id": target_id,
225+
"type": "string"
226+
}
227+
target_entity = GtsEntity(content=target_content, cfg=DEFAULT_GTS_CONFIG)
228+
229+
schema_id = "gts.vendor.good.ns.type.v1~"
230+
content = {
231+
"$schema": "http://json-schema.org/draft-07/schema#",
232+
"$id": schema_id,
233+
"properties": {
234+
"child": {
235+
"$ref": f"gts://{target_id}"
236+
}
237+
}
238+
}
239+
entity = GtsEntity(content=content, gts_id=GtsID(schema_id), is_schema=True)
240+
241+
store = GtsStore(MockReader([target_entity, entity]))
242+
243+
try:
244+
store.validate_schema(schema_id)
245+
except ValueError as e:
246+
if "$ref" in str(e):
247+
self.fail(f"gts:// ref should be accepted: {e}")
248+
249+
def test_validate_schema_refs_rejects_malformed_gts_id(self):
250+
"""Issue #32: Malformed GTS IDs after gts:// should be rejected."""
251+
store = GtsStore(None)
252+
253+
schema_id = "gts.vendor.bad.ns.type.v1~"
254+
content = {
255+
"$schema": "http://json-schema.org/draft-07/schema#",
256+
"$id": schema_id,
257+
"properties": {
258+
"child": {
259+
"$ref": "gts://invalid-gts-id"
260+
}
261+
}
262+
}
263+
entity = GtsEntity(content=content, gts_id=GtsID(schema_id), is_schema=True)
264+
store.register(entity)
265+
266+
with self.assertRaises(ValueError) as cm:
267+
store.validate_schema(schema_id)
268+
self.assertIn("invalid GTS identifier", str(cm.exception))
269+
270+
def test_validate_schema_refs_nested_invalid_ref(self):
271+
"""Issue #32: Nested invalid refs should report correct path."""
272+
store = GtsStore(None)
273+
274+
schema_id = "gts.vendor.bad.ns.type.v1~"
275+
content = {
276+
"$schema": "http://json-schema.org/draft-07/schema#",
277+
"$id": schema_id,
278+
"properties": {
279+
"level1": {
280+
"properties": {
281+
"level2": {
282+
"properties": {
283+
"level3": {
284+
"$ref": "invalid-external-ref"
285+
}
286+
}
287+
}
288+
}
289+
}
290+
}
291+
}
292+
entity = GtsEntity(content=content, gts_id=GtsID(schema_id), is_schema=True)
293+
store.register(entity)
294+
295+
with self.assertRaises(ValueError) as cm:
296+
store.validate_schema(schema_id)
297+
error_msg = str(cm.exception)
298+
self.assertIn("properties.level1.properties.level2.properties.level3.$ref", error_msg)
299+
300+
if __name__ == '__main__':
301+
unittest.main()

0 commit comments

Comments
 (0)