cocoindex 0.1.71__cp311-cp311-win_amd64.whl → 0.1.73__cp311-cp311-win_amd64.whl

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.
@@ -9,6 +9,10 @@ import pytest
9
9
  from numpy.typing import NDArray
10
10
 
11
11
  from cocoindex.typing import (
12
+ AnalyzedBasicType,
13
+ AnalyzedDictType,
14
+ AnalyzedListType,
15
+ AnalyzedStructType,
12
16
  AnalyzedTypeInfo,
13
17
  TypeAttr,
14
18
  TypeKind,
@@ -33,83 +37,67 @@ class SimpleNamedTuple(NamedTuple):
33
37
  def test_ndarray_float32_no_dim() -> None:
34
38
  typ = NDArray[np.float32]
35
39
  result = analyze_type_info(typ)
36
- assert result.kind == "Vector"
37
- assert result.vector_info == VectorInfo(dim=None)
38
- assert result.elem_type == np.float32
39
- assert result.key_type is None
40
- assert result.struct_type is None
40
+ assert isinstance(result.variant, AnalyzedListType)
41
+ assert result.variant.vector_info is None
42
+ assert result.variant.elem_type == np.float32
41
43
  assert result.nullable is False
42
- assert result.np_number_type is not None
43
- assert get_origin(result.np_number_type) == np.ndarray
44
- assert get_args(result.np_number_type)[1] == np.dtype[np.float32]
44
+ assert get_origin(result.core_type) == np.ndarray
45
+ assert get_args(result.core_type)[1] == np.dtype[np.float32]
45
46
 
46
47
 
47
48
  def test_vector_float32_no_dim() -> None:
48
49
  typ = Vector[np.float32]
49
50
  result = analyze_type_info(typ)
50
- assert result.kind == "Vector"
51
- assert result.vector_info == VectorInfo(dim=None)
52
- assert result.elem_type == np.float32
53
- assert result.key_type is None
54
- assert result.struct_type is None
51
+ assert isinstance(result.variant, AnalyzedListType)
52
+ assert result.variant.vector_info == VectorInfo(dim=None)
53
+ assert result.variant.elem_type == np.float32
55
54
  assert result.nullable is False
56
- assert result.np_number_type is not None
57
- assert get_origin(result.np_number_type) == np.ndarray
58
- assert get_args(result.np_number_type)[1] == np.dtype[np.float32]
55
+ assert get_origin(result.core_type) == np.ndarray
56
+ assert get_args(result.core_type)[1] == np.dtype[np.float32]
59
57
 
60
58
 
61
59
  def test_ndarray_float64_with_dim() -> None:
62
60
  typ = Annotated[NDArray[np.float64], VectorInfo(dim=128)]
63
61
  result = analyze_type_info(typ)
64
- assert result.kind == "Vector"
65
- assert result.vector_info == VectorInfo(dim=128)
66
- assert result.elem_type == np.float64
67
- assert result.key_type is None
68
- assert result.struct_type is None
62
+ assert isinstance(result.variant, AnalyzedListType)
63
+ assert result.variant.vector_info == VectorInfo(dim=128)
64
+ assert result.variant.elem_type == np.float64
69
65
  assert result.nullable is False
70
- assert result.np_number_type is not None
71
- assert get_origin(result.np_number_type) == np.ndarray
72
- assert get_args(result.np_number_type)[1] == np.dtype[np.float64]
66
+ assert get_origin(result.core_type) == np.ndarray
67
+ assert get_args(result.core_type)[1] == np.dtype[np.float64]
73
68
 
74
69
 
75
70
  def test_vector_float32_with_dim() -> None:
76
71
  typ = Vector[np.float32, Literal[384]]
77
72
  result = analyze_type_info(typ)
78
- assert result.kind == "Vector"
79
- assert result.vector_info == VectorInfo(dim=384)
80
- assert result.elem_type == np.float32
81
- assert result.key_type is None
82
- assert result.struct_type is None
73
+ assert isinstance(result.variant, AnalyzedListType)
74
+ assert result.variant.vector_info == VectorInfo(dim=384)
75
+ assert result.variant.elem_type == np.float32
83
76
  assert result.nullable is False
84
- assert result.np_number_type is not None
85
- assert get_origin(result.np_number_type) == np.ndarray
86
- assert get_args(result.np_number_type)[1] == np.dtype[np.float32]
77
+ assert get_origin(result.core_type) == np.ndarray
78
+ assert get_args(result.core_type)[1] == np.dtype[np.float32]
87
79
 
88
80
 
89
81
  def test_ndarray_int64_no_dim() -> None:
90
82
  typ = NDArray[np.int64]
91
83
  result = analyze_type_info(typ)
92
- assert result.kind == "Vector"
93
- assert result.vector_info == VectorInfo(dim=None)
94
- assert result.elem_type == np.int64
84
+ assert isinstance(result.variant, AnalyzedListType)
85
+ assert result.variant.vector_info is None
86
+ assert result.variant.elem_type == np.int64
95
87
  assert result.nullable is False
96
- assert result.np_number_type is not None
97
- assert get_origin(result.np_number_type) == np.ndarray
98
- assert get_args(result.np_number_type)[1] == np.dtype[np.int64]
88
+ assert get_origin(result.core_type) == np.ndarray
89
+ assert get_args(result.core_type)[1] == np.dtype[np.int64]
99
90
 
100
91
 
101
92
  def test_nullable_ndarray() -> None:
102
93
  typ = NDArray[np.float32] | None
103
94
  result = analyze_type_info(typ)
104
- assert result.kind == "Vector"
105
- assert result.vector_info == VectorInfo(dim=None)
106
- assert result.elem_type == np.float32
107
- assert result.key_type is None
108
- assert result.struct_type is None
95
+ assert isinstance(result.variant, AnalyzedListType)
96
+ assert result.variant.vector_info is None
97
+ assert result.variant.elem_type == np.float32
109
98
  assert result.nullable is True
110
- assert result.np_number_type is not None
111
- assert get_origin(result.np_number_type) == np.ndarray
112
- assert get_args(result.np_number_type)[1] == np.dtype[np.float32]
99
+ assert get_origin(result.core_type) == np.ndarray
100
+ assert get_args(result.core_type)[1] == np.dtype[np.float32]
113
101
 
114
102
 
115
103
  def test_scalar_numpy_types() -> None:
@@ -119,59 +107,38 @@ def test_scalar_numpy_types() -> None:
119
107
  (np.float64, "Float64"),
120
108
  ]:
121
109
  type_info = analyze_type_info(np_type)
122
- assert type_info.kind == expected_kind, (
123
- f"Expected {expected_kind} for {np_type}, got {type_info.kind}"
110
+ assert isinstance(type_info.variant, AnalyzedBasicType)
111
+ assert type_info.variant.kind == expected_kind, (
112
+ f"Expected {expected_kind} for {np_type}, got {type_info.variant.kind}"
124
113
  )
125
- assert type_info.np_number_type == np_type, (
126
- f"Expected {np_type}, got {type_info.np_number_type}"
114
+ assert type_info.core_type == np_type, (
115
+ f"Expected {np_type}, got {type_info.core_type}"
127
116
  )
128
- assert type_info.elem_type is None
129
- assert type_info.vector_info is None
130
117
 
131
118
 
132
119
  def test_vector_str() -> None:
133
120
  typ = Vector[str]
134
121
  result = analyze_type_info(typ)
135
- assert result.kind == "Vector"
136
- assert result.elem_type is str
137
- assert result.vector_info == VectorInfo(dim=None)
138
-
139
-
140
- def test_vector_complex64() -> None:
141
- typ = Vector[np.complex64]
142
- result = analyze_type_info(typ)
143
- assert result.kind == "Vector"
144
- assert result.elem_type == np.complex64
145
- assert result.vector_info == VectorInfo(dim=None)
122
+ assert isinstance(result.variant, AnalyzedListType)
123
+ assert result.variant.elem_type is str
124
+ assert result.variant.vector_info == VectorInfo(dim=None)
146
125
 
147
126
 
148
127
  def test_non_numpy_vector() -> None:
149
128
  typ = Vector[float, Literal[3]]
150
129
  result = analyze_type_info(typ)
151
- assert result.kind == "Vector"
152
- assert result.elem_type is float
153
- assert result.vector_info == VectorInfo(dim=3)
154
-
155
-
156
- def test_ndarray_any_dtype() -> None:
157
- typ = NDArray[Any]
158
- with pytest.raises(
159
- TypeError, match="NDArray for Vector must use a concrete numpy dtype"
160
- ):
161
- analyze_type_info(typ)
130
+ assert isinstance(result.variant, AnalyzedListType)
131
+ assert result.variant.elem_type is float
132
+ assert result.variant.vector_info == VectorInfo(dim=3)
162
133
 
163
134
 
164
135
  def test_list_of_primitives() -> None:
165
136
  typ = list[str]
166
137
  result = analyze_type_info(typ)
167
138
  assert result == AnalyzedTypeInfo(
168
- kind="Vector",
169
139
  core_type=list[str],
170
- vector_info=VectorInfo(dim=None),
171
- elem_type=str,
172
- key_type=None,
173
- struct_type=None,
174
- np_number_type=None,
140
+ base_type=list,
141
+ variant=AnalyzedListType(elem_type=str, vector_info=None),
175
142
  attrs=None,
176
143
  nullable=False,
177
144
  )
@@ -181,13 +148,9 @@ def test_list_of_structs() -> None:
181
148
  typ = list[SimpleDataclass]
182
149
  result = analyze_type_info(typ)
183
150
  assert result == AnalyzedTypeInfo(
184
- kind="LTable",
185
151
  core_type=list[SimpleDataclass],
186
- vector_info=None,
187
- elem_type=SimpleDataclass,
188
- key_type=None,
189
- struct_type=None,
190
- np_number_type=None,
152
+ base_type=list,
153
+ variant=AnalyzedListType(elem_type=SimpleDataclass, vector_info=None),
191
154
  attrs=None,
192
155
  nullable=False,
193
156
  )
@@ -197,13 +160,9 @@ def test_sequence_of_int() -> None:
197
160
  typ = Sequence[int]
198
161
  result = analyze_type_info(typ)
199
162
  assert result == AnalyzedTypeInfo(
200
- kind="Vector",
201
163
  core_type=Sequence[int],
202
- vector_info=VectorInfo(dim=None),
203
- elem_type=int,
204
- key_type=None,
205
- struct_type=None,
206
- np_number_type=None,
164
+ base_type=Sequence,
165
+ variant=AnalyzedListType(elem_type=int, vector_info=None),
207
166
  attrs=None,
208
167
  nullable=False,
209
168
  )
@@ -213,13 +172,9 @@ def test_list_with_vector_info() -> None:
213
172
  typ = Annotated[list[int], VectorInfo(dim=5)]
214
173
  result = analyze_type_info(typ)
215
174
  assert result == AnalyzedTypeInfo(
216
- kind="Vector",
217
175
  core_type=list[int],
218
- vector_info=VectorInfo(dim=5),
219
- elem_type=int,
220
- key_type=None,
221
- struct_type=None,
222
- np_number_type=None,
176
+ base_type=list,
177
+ variant=AnalyzedListType(elem_type=int, vector_info=VectorInfo(dim=5)),
223
178
  attrs=None,
224
179
  nullable=False,
225
180
  )
@@ -229,13 +184,9 @@ def test_dict_str_int() -> None:
229
184
  typ = dict[str, int]
230
185
  result = analyze_type_info(typ)
231
186
  assert result == AnalyzedTypeInfo(
232
- kind="KTable",
233
187
  core_type=dict[str, int],
234
- vector_info=None,
235
- elem_type=(str, int),
236
- key_type=None,
237
- struct_type=None,
238
- np_number_type=None,
188
+ base_type=dict,
189
+ variant=AnalyzedDictType(key_type=str, value_type=int),
239
190
  attrs=None,
240
191
  nullable=False,
241
192
  )
@@ -245,13 +196,9 @@ def test_mapping_str_dataclass() -> None:
245
196
  typ = Mapping[str, SimpleDataclass]
246
197
  result = analyze_type_info(typ)
247
198
  assert result == AnalyzedTypeInfo(
248
- kind="KTable",
249
199
  core_type=Mapping[str, SimpleDataclass],
250
- vector_info=None,
251
- elem_type=(str, SimpleDataclass),
252
- key_type=None,
253
- struct_type=None,
254
- np_number_type=None,
200
+ base_type=Mapping,
201
+ variant=AnalyzedDictType(key_type=str, value_type=SimpleDataclass),
255
202
  attrs=None,
256
203
  nullable=False,
257
204
  )
@@ -261,13 +208,9 @@ def test_dataclass() -> None:
261
208
  typ = SimpleDataclass
262
209
  result = analyze_type_info(typ)
263
210
  assert result == AnalyzedTypeInfo(
264
- kind="Struct",
265
211
  core_type=SimpleDataclass,
266
- vector_info=None,
267
- elem_type=None,
268
- key_type=None,
269
- struct_type=SimpleDataclass,
270
- np_number_type=None,
212
+ base_type=SimpleDataclass,
213
+ variant=AnalyzedStructType(struct_type=SimpleDataclass),
271
214
  attrs=None,
272
215
  nullable=False,
273
216
  )
@@ -277,29 +220,9 @@ def test_named_tuple() -> None:
277
220
  typ = SimpleNamedTuple
278
221
  result = analyze_type_info(typ)
279
222
  assert result == AnalyzedTypeInfo(
280
- kind="Struct",
281
223
  core_type=SimpleNamedTuple,
282
- vector_info=None,
283
- elem_type=None,
284
- key_type=None,
285
- struct_type=SimpleNamedTuple,
286
- np_number_type=None,
287
- attrs=None,
288
- nullable=False,
289
- )
290
-
291
-
292
- def test_tuple_key_value() -> None:
293
- typ = (str, int)
294
- result = analyze_type_info(typ)
295
- assert result == AnalyzedTypeInfo(
296
- kind="Int64",
297
- core_type=int,
298
- vector_info=None,
299
- elem_type=None,
300
- key_type=str,
301
- struct_type=None,
302
- np_number_type=None,
224
+ base_type=SimpleNamedTuple,
225
+ variant=AnalyzedStructType(struct_type=SimpleNamedTuple),
303
226
  attrs=None,
304
227
  nullable=False,
305
228
  )
@@ -309,13 +232,9 @@ def test_str() -> None:
309
232
  typ = str
310
233
  result = analyze_type_info(typ)
311
234
  assert result == AnalyzedTypeInfo(
312
- kind="Str",
313
235
  core_type=str,
314
- vector_info=None,
315
- elem_type=None,
316
- key_type=None,
317
- struct_type=None,
318
- np_number_type=None,
236
+ base_type=str,
237
+ variant=AnalyzedBasicType(kind="Str"),
319
238
  attrs=None,
320
239
  nullable=False,
321
240
  )
@@ -325,13 +244,9 @@ def test_bool() -> None:
325
244
  typ = bool
326
245
  result = analyze_type_info(typ)
327
246
  assert result == AnalyzedTypeInfo(
328
- kind="Bool",
329
247
  core_type=bool,
330
- vector_info=None,
331
- elem_type=None,
332
- key_type=None,
333
- struct_type=None,
334
- np_number_type=None,
248
+ base_type=bool,
249
+ variant=AnalyzedBasicType(kind="Bool"),
335
250
  attrs=None,
336
251
  nullable=False,
337
252
  )
@@ -341,13 +256,9 @@ def test_bytes() -> None:
341
256
  typ = bytes
342
257
  result = analyze_type_info(typ)
343
258
  assert result == AnalyzedTypeInfo(
344
- kind="Bytes",
345
259
  core_type=bytes,
346
- vector_info=None,
347
- elem_type=None,
348
- key_type=None,
349
- struct_type=None,
350
- np_number_type=None,
260
+ base_type=bytes,
261
+ variant=AnalyzedBasicType(kind="Bytes"),
351
262
  attrs=None,
352
263
  nullable=False,
353
264
  )
@@ -357,13 +268,9 @@ def test_uuid() -> None:
357
268
  typ = uuid.UUID
358
269
  result = analyze_type_info(typ)
359
270
  assert result == AnalyzedTypeInfo(
360
- kind="Uuid",
361
271
  core_type=uuid.UUID,
362
- vector_info=None,
363
- elem_type=None,
364
- key_type=None,
365
- struct_type=None,
366
- np_number_type=None,
272
+ base_type=uuid.UUID,
273
+ variant=AnalyzedBasicType(kind="Uuid"),
367
274
  attrs=None,
368
275
  nullable=False,
369
276
  )
@@ -373,13 +280,9 @@ def test_date() -> None:
373
280
  typ = datetime.date
374
281
  result = analyze_type_info(typ)
375
282
  assert result == AnalyzedTypeInfo(
376
- kind="Date",
377
283
  core_type=datetime.date,
378
- vector_info=None,
379
- elem_type=None,
380
- key_type=None,
381
- struct_type=None,
382
- np_number_type=None,
284
+ base_type=datetime.date,
285
+ variant=AnalyzedBasicType(kind="Date"),
383
286
  attrs=None,
384
287
  nullable=False,
385
288
  )
@@ -389,13 +292,9 @@ def test_time() -> None:
389
292
  typ = datetime.time
390
293
  result = analyze_type_info(typ)
391
294
  assert result == AnalyzedTypeInfo(
392
- kind="Time",
393
295
  core_type=datetime.time,
394
- vector_info=None,
395
- elem_type=None,
396
- key_type=None,
397
- struct_type=None,
398
- np_number_type=None,
296
+ base_type=datetime.time,
297
+ variant=AnalyzedBasicType(kind="Time"),
399
298
  attrs=None,
400
299
  nullable=False,
401
300
  )
@@ -405,13 +304,9 @@ def test_timedelta() -> None:
405
304
  typ = datetime.timedelta
406
305
  result = analyze_type_info(typ)
407
306
  assert result == AnalyzedTypeInfo(
408
- kind="TimeDelta",
409
307
  core_type=datetime.timedelta,
410
- vector_info=None,
411
- elem_type=None,
412
- key_type=None,
413
- struct_type=None,
414
- np_number_type=None,
308
+ base_type=datetime.timedelta,
309
+ variant=AnalyzedBasicType(kind="TimeDelta"),
415
310
  attrs=None,
416
311
  nullable=False,
417
312
  )
@@ -421,13 +316,9 @@ def test_float() -> None:
421
316
  typ = float
422
317
  result = analyze_type_info(typ)
423
318
  assert result == AnalyzedTypeInfo(
424
- kind="Float64",
425
319
  core_type=float,
426
- vector_info=None,
427
- elem_type=None,
428
- key_type=None,
429
- struct_type=None,
430
- np_number_type=None,
320
+ base_type=float,
321
+ variant=AnalyzedBasicType(kind="Float64"),
431
322
  attrs=None,
432
323
  nullable=False,
433
324
  )
@@ -437,13 +328,9 @@ def test_int() -> None:
437
328
  typ = int
438
329
  result = analyze_type_info(typ)
439
330
  assert result == AnalyzedTypeInfo(
440
- kind="Int64",
441
331
  core_type=int,
442
- vector_info=None,
443
- elem_type=None,
444
- key_type=None,
445
- struct_type=None,
446
- np_number_type=None,
332
+ base_type=int,
333
+ variant=AnalyzedBasicType(kind="Int64"),
447
334
  attrs=None,
448
335
  nullable=False,
449
336
  )
@@ -453,13 +340,9 @@ def test_type_with_attributes() -> None:
453
340
  typ = Annotated[str, TypeAttr("key", "value")]
454
341
  result = analyze_type_info(typ)
455
342
  assert result == AnalyzedTypeInfo(
456
- kind="Str",
457
343
  core_type=str,
458
- vector_info=None,
459
- elem_type=None,
460
- key_type=None,
461
- struct_type=None,
462
- np_number_type=None,
344
+ base_type=str,
345
+ variant=AnalyzedBasicType(kind="Str"),
463
346
  attrs={"key": "value"},
464
347
  nullable=False,
465
348
  )
@@ -494,7 +377,7 @@ def test_encode_enriched_type_ltable() -> None:
494
377
  typ = list[SimpleDataclass]
495
378
  result = encode_enriched_type(typ)
496
379
  assert result["type"]["kind"] == "LTable"
497
- assert result["type"]["row"]["kind"] == "Struct"
380
+ assert "fields" in result["type"]["row"]
498
381
  assert len(result["type"]["row"]["fields"]) == 2
499
382
 
500
383
 
@@ -525,22 +408,29 @@ def test_encode_scalar_numpy_types_schema() -> None:
525
408
  assert not schema.get("nullable", False)
526
409
 
527
410
 
528
- def test_invalid_struct_kind() -> None:
411
+ def test_annotated_struct_with_type_kind() -> None:
529
412
  typ = Annotated[SimpleDataclass, TypeKind("Vector")]
530
- with pytest.raises(ValueError, match="Unexpected type kind for struct: Vector"):
531
- analyze_type_info(typ)
413
+ result = analyze_type_info(typ)
414
+ assert isinstance(result.variant, AnalyzedBasicType)
415
+ assert result.variant.kind == "Vector"
532
416
 
533
417
 
534
- def test_invalid_list_kind() -> None:
418
+ def test_annotated_list_with_type_kind() -> None:
535
419
  typ = Annotated[list[int], TypeKind("Struct")]
536
- with pytest.raises(ValueError, match="Unexpected type kind for list: Struct"):
537
- analyze_type_info(typ)
420
+ result = analyze_type_info(typ)
421
+ assert isinstance(result.variant, AnalyzedBasicType)
422
+ assert result.variant.kind == "Struct"
538
423
 
539
424
 
540
425
  def test_unsupported_type() -> None:
541
- typ = set
542
426
  with pytest.raises(
543
427
  ValueError,
544
428
  match="Unsupported as a specific type annotation for CocoIndex data type.*: <class 'set'>",
545
429
  ):
546
- analyze_type_info(typ)
430
+ analyze_type_info(set)
431
+
432
+ with pytest.raises(
433
+ ValueError,
434
+ match="Unsupported as a specific type annotation for CocoIndex data type.*: <class 'numpy.complex64'>",
435
+ ):
436
+ Vector[np.complex64]
@@ -0,0 +1,134 @@
1
+ """Tests for naming validation functionality."""
2
+
3
+ import pytest
4
+ from cocoindex.validation import (
5
+ validate_field_name,
6
+ validate_flow_name,
7
+ validate_full_flow_name,
8
+ validate_app_namespace_name,
9
+ validate_target_name,
10
+ NamingError,
11
+ validate_identifier_name,
12
+ )
13
+
14
+
15
+ class TestValidateIdentifierName:
16
+ """Test the core validation function."""
17
+
18
+ def test_valid_names(self) -> None:
19
+ """Test that valid names pass validation."""
20
+ valid_names = [
21
+ "field1",
22
+ "field_name",
23
+ "_private",
24
+ "a",
25
+ "field123",
26
+ "FIELD_NAME",
27
+ "MyField",
28
+ "field_123_test",
29
+ ]
30
+
31
+ for name in valid_names:
32
+ result = validate_identifier_name(name)
33
+ assert result is None, f"Valid name '{name}' failed validation: {result}"
34
+
35
+ def test_valid_names_with_dots(self) -> None:
36
+ """Test that valid names with dots pass validation when allowed."""
37
+ valid_names = ["app.flow", "my_app.my_flow", "namespace.sub.flow", "a.b.c.d"]
38
+
39
+ for name in valid_names:
40
+ result = validate_identifier_name(name, allow_dots=True)
41
+ assert result is None, (
42
+ f"Valid dotted name '{name}' failed validation: {result}"
43
+ )
44
+
45
+ def test_invalid_starting_characters(self) -> None:
46
+ """Test names with invalid starting characters."""
47
+ invalid_names = [
48
+ "123field", # starts with digit
49
+ ".field", # starts with dot
50
+ "-field", # starts with dash
51
+ " field", # starts with space
52
+ ]
53
+
54
+ for name in invalid_names:
55
+ result = validate_identifier_name(name)
56
+ assert result is not None, (
57
+ f"Invalid name '{name}' should have failed validation"
58
+ )
59
+
60
+ def test_double_underscore_restriction(self) -> None:
61
+ """Test double underscore restriction."""
62
+ invalid_names = ["__reserved", "__internal", "__test"]
63
+
64
+ for name in invalid_names:
65
+ result = validate_identifier_name(name)
66
+ assert result is not None
67
+ assert "double underscores" in result.lower()
68
+
69
+ def test_length_restriction(self) -> None:
70
+ """Test maximum length restriction."""
71
+ long_name = "a" * 65
72
+ result = validate_identifier_name(long_name, max_length=64)
73
+ assert result is not None
74
+ assert "maximum length" in result.lower()
75
+
76
+
77
+ class TestSpecificValidators:
78
+ """Test the specific validation functions."""
79
+
80
+ def test_valid_field_names(self) -> None:
81
+ """Test valid field names."""
82
+ valid_names = ["field1", "field_name", "_private", "FIELD"]
83
+ for name in valid_names:
84
+ validate_field_name(name) # Should not raise
85
+
86
+ def test_invalid_field_names(self) -> None:
87
+ """Test invalid field names raise NamingError."""
88
+ invalid_names = ["123field", "field-name", "__reserved", "a" * 65]
89
+
90
+ for name in invalid_names:
91
+ with pytest.raises(NamingError):
92
+ validate_field_name(name)
93
+
94
+ def test_flow_validation(self) -> None:
95
+ """Test flow name validation."""
96
+ # Valid flow names
97
+ validate_flow_name("MyFlow")
98
+ validate_flow_name("my_flow_123")
99
+
100
+ # Invalid flow names
101
+ with pytest.raises(NamingError):
102
+ validate_flow_name("123flow")
103
+
104
+ with pytest.raises(NamingError):
105
+ validate_flow_name("__reserved_flow")
106
+
107
+ def test_full_flow_name_allows_dots(self) -> None:
108
+ """Test that full flow names allow dots."""
109
+ validate_full_flow_name("app.my_flow")
110
+ validate_full_flow_name("namespace.subnamespace.flow")
111
+
112
+ # But still reject invalid patterns
113
+ with pytest.raises(NamingError):
114
+ validate_full_flow_name("123.invalid")
115
+
116
+ def test_target_validation(self) -> None:
117
+ """Test target name validation."""
118
+ validate_target_name("my_target")
119
+ validate_target_name("output_table")
120
+
121
+ with pytest.raises(NamingError):
122
+ validate_target_name("123target")
123
+
124
+ def test_app_namespace_validation(self) -> None:
125
+ """Test app namespace validation."""
126
+ validate_app_namespace_name("myapp")
127
+ validate_app_namespace_name("my_app_123")
128
+
129
+ # Should not allow dots in app namespace
130
+ with pytest.raises(NamingError):
131
+ validate_app_namespace_name("my.app")
132
+
133
+ with pytest.raises(NamingError):
134
+ validate_app_namespace_name("123app")