@@ -534,6 +534,8 @@ def test_validate_docker_compose_yml_invalid_image_dockerfile(mocker):
534534 return_value = True ,
535535 )
536536 mocked_open = mocker .MagicMock ()
537+ mocked_open .__enter__ = mocker .MagicMock (return_value = mocked_open )
538+ mocked_open .__exit__ = mocker .MagicMock (return_value = False )
537539 mocked_open .read .return_value = 'FROM cloudblueconnect/connect-extension-runner:0.3\n '
538540 mocker .patch (
539541 'connect.eaas.core.validation.validators.base.open' ,
@@ -566,6 +568,155 @@ def test_validate_docker_compose_yml_invalid_image_dockerfile(mocker):
566568 assert item .file == 'fake_dir/Dockerfile'
567569
568570
571+ def test_validate_docker_compose_yml_multistage_image_dockerfile (mocker ):
572+ mocker .patch (
573+ 'connect.eaas.core.validation.validators.base.os.path.isfile' ,
574+ return_value = True ,
575+ )
576+ mocked_open = mocker .MagicMock ()
577+ mocked_open .__enter__ = mocker .MagicMock (return_value = mocked_open )
578+ mocked_open .__exit__ = mocker .MagicMock (return_value = False )
579+ mocked_open .read .return_value = (
580+ 'FROM cloudblueconnect/connect-extension-runner:0.3 as a-stage-name\n '
581+ )
582+ mocker .patch (
583+ 'connect.eaas.core.validation.validators.base.open' ,
584+ side_effect = [None , mocked_open ],
585+ )
586+ mocker .patch (
587+ 'connect.eaas.core.validation.validators.base.yaml.safe_load' ,
588+ return_value = {
589+ 'services' : {
590+ 'dev' : {
591+ 'build' : {'dockerfile' : 'Dockerfile' },
592+ },
593+ },
594+ },
595+ )
596+
597+ result = validate_docker_compose_yml ({'project_dir' : 'fake_dir' , 'runner_version' : '0.3' })
598+
599+ assert isinstance (result , ValidationResult )
600+ assert result .must_exit is False
601+ assert len (result .items ) == 0
602+
603+
604+ def test_validate_docker_compose_yml_dockerfile_case_insensitive (mocker ):
605+ mocker .patch (
606+ 'connect.eaas.core.validation.validators.base.os.path.isfile' ,
607+ return_value = True ,
608+ )
609+ mocked_open = mocker .MagicMock ()
610+ mocked_open .__enter__ = mocker .MagicMock (return_value = mocked_open )
611+ mocked_open .__exit__ = mocker .MagicMock (return_value = False )
612+ mocked_open .read .return_value = (
613+ 'FROM CloudBlueConnect/Connect-Extension-Runner:1.0\n '
614+ )
615+ mocker .patch (
616+ 'connect.eaas.core.validation.validators.base.open' ,
617+ side_effect = [None , mocked_open ],
618+ )
619+ mocker .patch (
620+ 'connect.eaas.core.validation.validators.base.yaml.safe_load' ,
621+ return_value = {
622+ 'services' : {
623+ 'dev' : {
624+ 'build' : {'dockerfile' : 'Dockerfile' },
625+ },
626+ },
627+ },
628+ )
629+
630+ result = validate_docker_compose_yml ({'project_dir' : 'fake_dir' , 'runner_version' : '1.0' })
631+
632+ assert isinstance (result , ValidationResult )
633+ assert result .must_exit is False
634+ assert len (result .items ) == 0
635+
636+
637+ def test_validate_docker_compose_yml_multistage_runner_in_last_stage (mocker ):
638+ mocker .patch (
639+ 'connect.eaas.core.validation.validators.base.os.path.isfile' ,
640+ return_value = True ,
641+ )
642+ mocked_open = mocker .MagicMock ()
643+ mocked_open .__enter__ = mocker .MagicMock (return_value = mocked_open )
644+ mocked_open .__exit__ = mocker .MagicMock (return_value = False )
645+ mocked_open .read .return_value = (
646+ 'FROM python:3.10 AS builder\n '
647+ 'RUN pip install stuff\n '
648+ '\n '
649+ 'FROM cloudblueconnect/connect-extension-runner:0.3\n '
650+ 'COPY --from=builder /app /app\n '
651+ )
652+ mocker .patch (
653+ 'connect.eaas.core.validation.validators.base.open' ,
654+ side_effect = [None , mocked_open ],
655+ )
656+ mocker .patch (
657+ 'connect.eaas.core.validation.validators.base.yaml.safe_load' ,
658+ return_value = {
659+ 'services' : {
660+ 'dev' : {
661+ 'build' : {'dockerfile' : 'Dockerfile' },
662+ },
663+ },
664+ },
665+ )
666+
667+ result = validate_docker_compose_yml ({'project_dir' : 'fake_dir' , 'runner_version' : '0.3' })
668+
669+ assert isinstance (result , ValidationResult )
670+ assert result .must_exit is False
671+ assert len (result .items ) == 0
672+
673+
674+ def test_validate_docker_compose_yml_multistage_wrong_last_stage (mocker ):
675+ mocker .patch (
676+ 'connect.eaas.core.validation.validators.base.os.path.isfile' ,
677+ return_value = True ,
678+ )
679+ mocked_open = mocker .MagicMock ()
680+ mocked_open .__enter__ = mocker .MagicMock (return_value = mocked_open )
681+ mocked_open .__exit__ = mocker .MagicMock (return_value = False )
682+ mocked_open .read .return_value = (
683+ 'FROM cloudblueconnect/connect-extension-runner:0.3 AS base\n '
684+ 'RUN pip install stuff\n '
685+ '\n '
686+ 'FROM python:3.10\n '
687+ 'COPY --from=base /app /app\n '
688+ )
689+ mocker .patch (
690+ 'connect.eaas.core.validation.validators.base.open' ,
691+ side_effect = [None , mocked_open ],
692+ )
693+ mocker .patch (
694+ 'connect.eaas.core.validation.validators.base.yaml.safe_load' ,
695+ return_value = {
696+ 'services' : {
697+ 'dev' : {
698+ 'build' : {'dockerfile' : 'Dockerfile' },
699+ },
700+ },
701+ },
702+ )
703+
704+ result = validate_docker_compose_yml ({'project_dir' : 'fake_dir' , 'runner_version' : '0.3' })
705+
706+ assert isinstance (result , ValidationResult )
707+ assert result .must_exit is False
708+ assert len (result .items ) == 1
709+ item = result .items [0 ]
710+ assert isinstance (item , ValidationItem )
711+ assert item .level == 'ERROR'
712+ assert (
713+ 'Invalid base image in Dockerfile of service *dev*: expected '
714+ '*cloudblueconnect/connect-extension-runner:0.3* '
715+ 'got *python:3.10*.'
716+ ) in item .message
717+ assert item .file == 'fake_dir/Dockerfile'
718+
719+
569720def test_validate_docker_compose_yml_invalid_image_no_dockerfile (mocker ):
570721 mocker .patch (
571722 'connect.eaas.core.validation.validators.base.os.path.isfile' ,
0 commit comments