@@ -644,3 +644,191 @@ func TestAppBrickInstanceModelsDetails(t *testing.T) {
644644 })
645645 }
646646}
647+
648+ func TestAppBrickInstancesList (t * testing.T ) {
649+
650+ bIndex := & bricksindex.BricksIndex {
651+ Bricks : []bricksindex.Brick {
652+ {
653+ ID : "arduino:weather_forecast" ,
654+ Name : "Weather Forecast" ,
655+ Category : "miscellaneous" ,
656+ RequireModel : false ,
657+ Variables : []bricksindex.BrickVariable {},
658+ },
659+ {
660+ ID : "arduino:object_detection" ,
661+ Name : "Object Detection" ,
662+ Category : "video" ,
663+ ModelName : "yolox-object-detection" ,
664+ RequireModel : true ,
665+ Variables : []bricksindex.BrickVariable {
666+ {Name : "CUSTOM_MODEL_PATH" , DefaultValue : "/home/arduino/.arduino-bricks/ei-models" , Description : "path to the custom model directory" },
667+ {Name : "EI_OBJ_DETECTION_MODEL" , DefaultValue : "/models/ootb/ei/yolo-x-nano.eim" , Description : "path to the model file" },
668+ },
669+ },
670+ {
671+ ID : "arduino:audio_classification" ,
672+ Name : "Audio Classification" ,
673+ Category : "audio" ,
674+ ModelName : "glass-breaking" ,
675+ RequireModel : true ,
676+ Variables : []bricksindex.BrickVariable {
677+ {Name : "CUSTOM_MODEL_PATH" , DefaultValue : "/home/arduino/.arduino-bricks/ei-models" },
678+ {Name : "EI_AUDIO_CLASSIFICATION_MODEL" , DefaultValue : "/models/ootb/ei/glass-breaking.eim" },
679+ },
680+ },
681+ {
682+ ID : "arduino:streamlit_ui" ,
683+ Name : "WebUI - Streamlit" ,
684+ Category : "ui" ,
685+ RequireModel : false ,
686+ Ports : []string {"7000" , "8000" },
687+ },
688+ },
689+ }
690+
691+ svc := & Service {
692+ bricksIndex : bIndex ,
693+ modelsIndex : & modelsindex.ModelsIndex {},
694+ }
695+
696+ tests := []struct {
697+ name string
698+ app * app.ArduinoApp
699+ expectedError string
700+ validate func (* testing.T , AppBrickInstancesResult )
701+ }{
702+ {
703+ name : "Error - Brick not found in Index" ,
704+ app : & app.ArduinoApp {
705+ Descriptor : app.AppDescriptor {
706+ Bricks : []app.Brick {
707+ {ID : "arduino:non_existent_brick" },
708+ },
709+ },
710+ },
711+ expectedError : "brick not found with id arduino:non_existent_brick" ,
712+ },
713+ {
714+ name : "Success - Empty App" ,
715+ app : & app.ArduinoApp {
716+ Descriptor : app.AppDescriptor {
717+ Bricks : []app.Brick {},
718+ },
719+ },
720+ validate : func (t * testing.T , res AppBrickInstancesResult ) {
721+ require .Empty (t , res .BrickInstances )
722+ },
723+ },
724+ {
725+ name : "Success - Simple Brick" ,
726+ app : & app.ArduinoApp {
727+ Descriptor : app.AppDescriptor {
728+ Bricks : []app.Brick {
729+ {ID : "arduino:weather_forecast" },
730+ },
731+ },
732+ },
733+ validate : func (t * testing.T , res AppBrickInstancesResult ) {
734+ require .Len (t , res .BrickInstances , 1 )
735+ brick := res .BrickInstances [0 ]
736+
737+ require .Equal (t , "arduino:weather_forecast" , brick .ID )
738+ require .Equal (t , "Weather Forecast" , brick .Name )
739+ require .Equal (t , "miscellaneous" , brick .Category )
740+ require .Equal (t , "installed" , brick .Status )
741+ require .Equal (t , "Arduino" , brick .Author )
742+ require .False (t , brick .RequireModel )
743+ require .Empty (t , brick .ModelID )
744+ },
745+ },
746+ {
747+ name : "Success - Brick with Model Configured" ,
748+ app : & app.ArduinoApp {
749+ Descriptor : app.AppDescriptor {
750+ Bricks : []app.Brick {
751+ {
752+ ID : "arduino:object_detection" ,
753+ Model : "face-detection" , // default model overridden
754+ Variables : map [string ]string {
755+ "CUSTOM_MODEL_PATH" : "/custom/path" ,
756+ },
757+ },
758+ },
759+ },
760+ },
761+ validate : func (t * testing.T , res AppBrickInstancesResult ) {
762+ require .Len (t , res .BrickInstances , 1 )
763+ brick := res .BrickInstances [0 ]
764+
765+ require .Equal (t , "arduino:object_detection" , brick .ID )
766+ require .Equal (t , "video" , brick .Category )
767+ require .True (t , brick .RequireModel )
768+ require .Equal (t , "face-detection" , brick .ModelID )
769+
770+ foundCustom := false
771+ for _ , v := range brick .ConfigVariables {
772+ if v .Name == "CUSTOM_MODEL_PATH" {
773+ require .Equal (t , "/custom/path" , v .Value )
774+ foundCustom = true
775+ }
776+ }
777+ require .True (t , foundCustom , "Variable CUSTOM_MODEL_PATH should be present and overridden" )
778+ },
779+ },
780+ {
781+ name : "Success - Multiple Bricks" ,
782+ app : & app.ArduinoApp {
783+ Descriptor : app.AppDescriptor {
784+ Bricks : []app.Brick {
785+ {ID : "arduino:streamlit_ui" },
786+ {ID : "arduino:audio_classification" , Model : "glass-breaking" },
787+ },
788+ },
789+ },
790+ validate : func (t * testing.T , res AppBrickInstancesResult ) {
791+ require .Len (t , res .BrickInstances , 2 )
792+
793+ // Brick 1: Streamlit UI
794+ b1 := res .BrickInstances [0 ]
795+ require .Equal (t , "arduino:streamlit_ui" , b1 .ID )
796+ require .Equal (t , "WebUI - Streamlit" , b1 .Name )
797+ require .Equal (t , "Arduino" , b1 .Author )
798+ require .Equal (t , "ui" , b1 .Category )
799+ require .Equal (t , "installed" , b1 .Status )
800+ require .Equal (t , "" , b1 .ModelID )
801+ require .Empty (t , b1 .Variables )
802+ require .Empty (t , b1 .ConfigVariables )
803+ require .False (t , b1 .RequireModel )
804+
805+ // Brick 2: Audio Classification
806+ b2 := res .BrickInstances [1 ]
807+ require .Equal (t , "arduino:audio_classification" , b2 .ID )
808+ require .Equal (t , "audio" , b2 .Category )
809+ require .True (t , b2 .RequireModel )
810+ require .Equal (t , "glass-breaking" , b2 .ModelID )
811+ require .Equal (t , 2 , len (b2 .ConfigVariables ))
812+ require .Equal (t , "/home/arduino/.arduino-bricks/ei-models" , b2 .ConfigVariables [0 ].Value )
813+ require .Equal (t , "/models/ootb/ei/glass-breaking.eim" , b2 .ConfigVariables [1 ].Value )
814+ },
815+ },
816+ }
817+
818+ for _ , tt := range tests {
819+ t .Run (tt .name , func (t * testing.T ) {
820+ result , err := svc .AppBrickInstancesList (tt .app )
821+
822+ if tt .expectedError != "" {
823+ require .Error (t , err )
824+ require .Contains (t , err .Error (), tt .expectedError )
825+ return
826+ }
827+
828+ require .NoError (t , err )
829+ if tt .validate != nil {
830+ tt .validate (t , result )
831+ }
832+ })
833+ }
834+ }
0 commit comments