@@ -431,9 +431,173 @@ def test_input_combinations(
431431 )
432432 assert request .get_input_source () == expected
433433
434- # -------------------------------------------------------------------------
435- # Edge cases
436- # -------------------------------------------------------------------------
434+
435+ # ---------------------------------------------------------------------------
436+ # Character pattern validation tests — RlsapiV1SystemInfo
437+ # ---------------------------------------------------------------------------
438+
439+
440+ class TestSystemInfoCharacterValidation :
441+ """Test character pattern validation for RlsapiV1SystemInfo fields."""
442+
443+ @pytest .mark .parametrize (
444+ ("field" , "value" ),
445+ [
446+ pytest .param ("os" , "RHEL" , id = "os-simple" ),
447+ pytest .param ("os" , "Red Hat Enterprise Linux" , id = "os-with-spaces" ),
448+ pytest .param ("os" , "CentOS-Stream" , id = "os-with-hyphen" ),
449+ pytest .param ("os" , "RHEL_9" , id = "os-with-underscore" ),
450+ pytest .param ("os" , "" , id = "os-empty-default" ),
451+ pytest .param ("version" , "9.3" , id = "version-major-minor" ),
452+ pytest .param ("version" , "8.10" , id = "version-two-digit-minor" ),
453+ pytest .param ("version" , "9.3.0" , id = "version-three-part" ),
454+ pytest .param ("arch" , "x86_64" , id = "arch-x86" ),
455+ pytest .param ("arch" , "aarch64" , id = "arch-arm" ),
456+ pytest .param ("arch" , "ppc64le" , id = "arch-ppc" ),
457+ pytest .param ("arch" , "s390x" , id = "arch-s390" ),
458+ ],
459+ )
460+ def test_valid_values_accepted (self , field : str , value : str ) -> None :
461+ """Test that valid system info values are accepted."""
462+ sysinfo = RlsapiV1SystemInfo (** {field : value })
463+ assert getattr (sysinfo , field ) == value
464+
465+ @pytest .mark .parametrize (
466+ ("field" , "value" ),
467+ [
468+ pytest .param ("os" , "<script>alert('xss')</script>" , id = "os-html-script" ),
469+ pytest .param ("os" , "RHEL\n " , id = "os-newline" ),
470+ pytest .param ("os" , "RHEL\t " , id = "os-tab" ),
471+ pytest .param ("os" , "RHEL\x00 " , id = "os-null-byte" ),
472+ pytest .param ("version" , "9.3<br>" , id = "version-html-tag" ),
473+ pytest .param ("version" , "9.3\r \n " , id = "version-crlf" ),
474+ pytest .param ("arch" , "x86_64; rm -rf /" , id = "arch-semicolon" ),
475+ pytest .param ("arch" , "x86_64\x0b " , id = "arch-vertical-tab" ),
476+ ],
477+ )
478+ def test_invalid_values_rejected (self , field : str , value : str ) -> None :
479+ """Test that invalid characters are rejected in system info fields."""
480+ with pytest .raises (ValidationError , match = "String should match pattern" ):
481+ RlsapiV1SystemInfo (** {field : value })
482+
483+ @pytest .mark .parametrize (
484+ "value" ,
485+ [
486+ pytest .param ("01JDKR8N7QW9ZMXVGK3PB5TQWZ" , id = "ulid" ),
487+ pytest .param ("550e8400-e29b-41d4-a716-446655440000" , id = "uuid" ),
488+ pytest .param ("machine-001" , id = "hostname-style" ),
489+ pytest .param ("" , id = "empty-default" ),
490+ ],
491+ )
492+ def test_valid_system_id (self , value : str ) -> None :
493+ """Test that valid machine IDs are accepted."""
494+ sysinfo = RlsapiV1SystemInfo (
495+ system_id = value # pyright: ignore[reportCallIssue]
496+ )
497+ assert sysinfo .system_id == value
498+
499+ @pytest .mark .parametrize (
500+ "value" ,
501+ [
502+ pytest .param ("machine id with spaces" , id = "spaces" ),
503+ pytest .param ("id<script>" , id = "html-tag" ),
504+ pytest .param ("id\n injection" , id = "newline" ),
505+ ],
506+ )
507+ def test_invalid_system_id (self , value : str ) -> None :
508+ """Test that invalid characters are rejected in system_id."""
509+ with pytest .raises (ValidationError , match = "String should match pattern" ):
510+ RlsapiV1SystemInfo (system_id = value ) # pyright: ignore[reportCallIssue]
511+
512+
513+ # ---------------------------------------------------------------------------
514+ # Character pattern validation tests — RlsapiV1CLA
515+ # ---------------------------------------------------------------------------
516+
517+
518+ class TestCLACharacterValidation :
519+ """Test character pattern validation for RlsapiV1CLA fields."""
520+
521+ @pytest .mark .parametrize (
522+ ("field" , "value" ),
523+ [
524+ pytest .param (
525+ "nevra" ,
526+ "command-line-assistant-0:0.2.0-1.el9.noarch" ,
527+ id = "nevra-with-epoch" ,
528+ ),
529+ pytest .param (
530+ "nevra" ,
531+ "command-line-assistant-0.1.0-1.el9.noarch" ,
532+ id = "nevra-without-epoch" ,
533+ ),
534+ pytest .param (
535+ "nevra" ,
536+ "pkg~pre1+post1-0:1.0-1.el9.x86_64" ,
537+ id = "nevra-tilde-plus" ,
538+ ),
539+ pytest .param ("nevra" , "" , id = "nevra-empty-default" ),
540+ pytest .param ("version" , "0.2.0" , id = "version-semver" ),
541+ pytest .param ("version" , "1.0.0-rc1" , id = "version-prerelease" ),
542+ pytest .param ("version" , "0.2.0.dev1" , id = "version-dev" ),
543+ pytest .param ("version" , "" , id = "version-empty-default" ),
544+ ],
545+ )
546+ def test_valid_values_accepted (self , field : str , value : str ) -> None :
547+ """Test that valid CLA values are accepted."""
548+ cla = RlsapiV1CLA (** {field : value })
549+ assert getattr (cla , field ) == value
550+
551+ @pytest .mark .parametrize (
552+ ("field" , "value" ),
553+ [
554+ pytest .param ("nevra" , "pkg<script>" , id = "nevra-html-tag" ),
555+ pytest .param ("nevra" , "pkg\n newline" , id = "nevra-newline" ),
556+ pytest .param ("nevra" , "pkg name spaces" , id = "nevra-spaces" ),
557+ pytest .param ("version" , "0.2.0\x00 " , id = "version-null-byte" ),
558+ pytest .param ("version" , "0.2.0<br>" , id = "version-html" ),
559+ pytest .param ("version" , "0.2.0\t " , id = "version-tab" ),
560+ ],
561+ )
562+ def test_invalid_values_rejected (self , field : str , value : str ) -> None :
563+ """Test that invalid characters are rejected in CLA fields."""
564+ with pytest .raises (ValidationError , match = "String should match pattern" ):
565+ RlsapiV1CLA (** {field : value })
566+
567+
568+ # ---------------------------------------------------------------------------
569+ # get_input_source() tests (continued edge cases)
570+ # ---------------------------------------------------------------------------
571+
572+
573+ class TestGetInputSourceEdgeCases :
574+ """Edge case tests for RlsapiV1InferRequest.get_input_source()."""
575+
576+ @pytest .fixture (name = "make_request" )
577+ def make_request_fixture (self ) -> Any :
578+ """Factory fixture to build requests with specific context values."""
579+
580+ class _RequestBuilder : # pylint: disable=too-few-public-methods
581+ """Helper to construct requests with variable context."""
582+
583+ @staticmethod
584+ def build (
585+ question : str = "q" ,
586+ stdin : str = "" ,
587+ attachment : str = "" ,
588+ terminal : str = "" ,
589+ ) -> RlsapiV1InferRequest :
590+ """Build an RlsapiV1InferRequest with specified context values."""
591+ return RlsapiV1InferRequest (
592+ question = question ,
593+ context = RlsapiV1Context (
594+ stdin = stdin ,
595+ attachments = RlsapiV1Attachment (contents = attachment ),
596+ terminal = RlsapiV1Terminal (output = terminal ),
597+ ),
598+ )
599+
600+ return _RequestBuilder
437601
438602 def test_preserves_content_formatting (self , make_request : Any ) -> None :
439603 """Test that content formatting (newlines, special chars) is preserved."""
0 commit comments