@@ -70,6 +70,10 @@ def make_build_info(
7070 git_hash = "abc123def456" ,
7171 board = "MatekH743" ,
7272 selected_features = None ,
73+ vehicle_name = None ,
74+ board_name = None ,
75+ version_name = "4.5.0" ,
76+ version_type = "stable" ,
7377 state = bm .BuildState .PENDING ,
7478 percent = 0 ,
7579):
@@ -80,6 +84,10 @@ def make_build_info(
8084 git_hash = git_hash ,
8185 board = board ,
8286 selected_features = set (selected_features ) if selected_features is not None else set (),
87+ vehicle_name = vehicle_name or vehicle_id .capitalize (),
88+ board_name = board_name or board ,
89+ version_name = version_name ,
90+ version_type = version_type ,
8391 )
8492 info .progress = bm .BuildProgress (state = state , percent = percent )
8593 return info
@@ -161,6 +169,21 @@ def test_create_build_raises_value_error_for_missing_vehicle_id(self, service):
161169 with pytest .raises (ValueError , match = "vehicle_id is required" ):
162170 service .create_build (request )
163171
172+ def test_create_build_raises_value_error_for_invalid_vehicle (
173+ self , service , mock_vehicles_manager
174+ ):
175+ """ValueError is raised when vehicle_id is not a known vehicle."""
176+ mock_vehicles_manager .get_vehicle_by_id .return_value = None
177+ request = BuildRequest (
178+ vehicle_id = "unknown-vehicle" ,
179+ board_id = "MatekH743" ,
180+ version_id = "copter-4.5.0-stable" ,
181+ selected_features = [],
182+ )
183+
184+ with pytest .raises (ValueError , match = "Invalid vehicle_id" ):
185+ service .create_build (request )
186+
164187 def test_create_build_raises_value_error_for_missing_board_id (
165188 self , service
166189 ):
@@ -216,9 +239,9 @@ def test_create_build_raises_value_error_when_remote_not_found(
216239 """ValueError is raised when the remote is not found."""
217240 mock_versions_manager .get_remote_info .return_value = None
218241 request = BuildRequest (
219- vehicle_id = "some-vehicle " ,
220- board_id = "some-board " ,
221- version_id = "some-version " ,
242+ vehicle_id = "copter " ,
243+ board_id = "MatekH743 " ,
244+ version_id = "copter-4.5.0-stable " ,
222245 selected_features = [],
223246 )
224247
@@ -725,3 +748,135 @@ def test_get_artifact_path_uses_correct_build_id(
725748 service .get_artifact_path ("my-target-build" )
726749
727750 mock_build_manager .build_exists .assert_called_once_with ("my-target-build" )
751+
752+ def test_get_build_includes_version_name_and_type (
753+ self ,
754+ service ,
755+ mock_build_manager ,
756+ mock_versions_manager ,
757+ ):
758+ """version.name and version.type come from versions_manager."""
759+ mock_build_manager .build_exists .return_value = True
760+ mock_build_manager .get_build_info .return_value = make_build_info ()
761+ mock_versions_manager .get_version_info .return_value = make_version_info (
762+ release_type = "stable" ,
763+ version_number = "4.5.0" ,
764+ )
765+
766+ result = service .get_build ("build-abc123" )
767+
768+ assert result .version .name == "4.5.0"
769+ assert result .version .type == "stable"
770+ mock_versions_manager .get_version_info .assert_called_with (
771+ vehicle_id = "copter" ,
772+ version_id = "copter-4.5.0-stable" ,
773+ )
774+
775+ def test_get_build_maps_latest_version_name_to_master (
776+ self ,
777+ service ,
778+ mock_build_manager ,
779+ mock_versions_manager ,
780+ ):
781+ """latest release_type is exposed as display name 'master'."""
782+ mock_build_manager .build_exists .return_value = True
783+ mock_build_manager .get_build_info .return_value = make_build_info (
784+ version_id = "copter-latest" ,
785+ )
786+ mock_versions_manager .get_version_info .return_value = make_version_info (
787+ release_type = "latest" ,
788+ version_number = "NA" ,
789+ )
790+
791+ result = service .get_build ("build-abc123" )
792+
793+ assert result .version .name == "master"
794+ assert result .version .type == "latest"
795+
796+ def test_get_build_falls_back_to_stored_version_when_unknown (
797+ self ,
798+ service ,
799+ mock_build_manager ,
800+ mock_versions_manager ,
801+ ):
802+ """Missing live version info uses names stored on BuildInfo."""
803+ mock_build_manager .build_exists .return_value = True
804+ mock_build_manager .get_build_info .return_value = make_build_info (
805+ version_name = "4.5.0" ,
806+ version_type = "stable" ,
807+ )
808+ mock_versions_manager .get_version_info .return_value = None
809+
810+ result = service .get_build ("build-abc123" )
811+
812+ assert result .version .name == "4.5.0"
813+ assert result .version .type == "stable"
814+
815+ def test_get_build_falls_back_to_stored_vehicle_name_when_unknown (
816+ self ,
817+ service ,
818+ mock_build_manager ,
819+ mock_vehicles_manager ,
820+ ):
821+ """Missing live vehicle uses display name stored on BuildInfo, not id."""
822+ mock_build_manager .build_exists .return_value = True
823+ mock_build_manager .get_build_info .return_value = make_build_info (
824+ vehicle_id = "retired-vehicle" ,
825+ vehicle_name = "Retired Vehicle" ,
826+ )
827+ mock_vehicles_manager .get_vehicle_by_id .side_effect = lambda _vid : None
828+
829+ result = service .get_build ("build-abc123" )
830+
831+ assert result .vehicle .id == "retired-vehicle"
832+ assert result .vehicle .name == "Retired Vehicle"
833+
834+ def test_get_build_config_yaml_generates_from_build_info (
835+ self ,
836+ service ,
837+ mock_build_manager ,
838+ ):
839+ mock_build_manager .build_exists .return_value = True
840+ mock_build_manager .get_build_info .return_value = make_build_info (
841+ state = bm .BuildState .SUCCESS ,
842+ vehicle_id = "copter" ,
843+ board = "MatekH743" ,
844+ vehicle_name = "Copter" ,
845+ selected_features = ["HAL_LOGGING_ENABLED" ],
846+ )
847+
848+ result = service .get_build_config_yaml ("build-abc123" )
849+
850+ assert result is not None
851+ yaml_text , filename = result
852+ assert "HAL_LOGGING_ENABLED" in yaml_text
853+ assert filename .endswith (".yaml" )
854+
855+ def test_get_build_config_yaml_returns_none_when_build_missing (
856+ self , service , mock_build_manager
857+ ):
858+ mock_build_manager .build_exists .return_value = False
859+ assert service .get_build_config_yaml ("missing" ) is None
860+
861+ def test_get_build_config_yaml_returns_none_when_info_missing (
862+ self , service , mock_build_manager
863+ ):
864+ mock_build_manager .build_exists .return_value = True
865+ mock_build_manager .get_build_info .return_value = None
866+ assert service .get_build_config_yaml ("build-abc123" ) is None
867+
868+ def test_create_build_stores_display_metadata (
869+ self , service , mock_build_manager
870+ ):
871+ service .create_build (
872+ BuildRequest (
873+ vehicle_id = "copter" ,
874+ board_id = "MatekH743" ,
875+ version_id = "copter-4.5.0-stable" ,
876+ selected_features = ["HAL_LOGGING_ENABLED" ],
877+ )
878+ )
879+ submitted : bm .BuildInfo = mock_build_manager .submit_build .call_args [1 ]["build_info" ]
880+ assert submitted .selected_features == {"HAL_LOGGING_ENABLED" }
881+ assert submitted .vehicle_name == "Copter"
882+ assert submitted .version_type == "stable"
0 commit comments