@@ -29,6 +29,7 @@ type testEnv struct {
2929 ctx context.Context
3030 registryURL string
3131 client * desktop.Client
32+ net * testcontainers.DockerNetwork
3233}
3334
3435// modelInfo contains all the information needed to generate model references
@@ -128,6 +129,7 @@ func setupTestEnv(t *testing.T) *testEnv {
128129 ctx : ctx ,
129130 registryURL : registryURL ,
130131 client : client ,
132+ net : net ,
131133 }
132134}
133135
@@ -627,3 +629,150 @@ func TestIntegration_TagModel(t *testing.T) {
627629 require .NoError (t , err )
628630 require .Empty (t , strings .TrimSpace (models ), "Model should be removed" )
629631}
632+
633+ // TestIntegration_PushModel tests pushing a model to registries with various reference formats
634+ // to ensure proper reference normalization and successful push operations.
635+ func TestIntegration_PushModel (t * testing.T ) {
636+ env := setupTestEnv (t )
637+
638+ // Set up custom registry with different alias
639+ t .Log ("Starting custom OCI registry container..." )
640+ customRegistryCtr , err := tc .Run (context .Background (), "registry:3" ,
641+ network .WithNetwork ([]string {"custom-registry.local" }, env .net ),
642+ )
643+ require .NoError (t , err )
644+ testcontainers .CleanupContainer (t , customRegistryCtr )
645+
646+ customRegistryEndpoint , err := customRegistryCtr .Endpoint (t .Context (), "" )
647+ require .NoError (t , err )
648+ customRegistryURL := fmt .Sprintf ("http://%s" , customRegistryEndpoint )
649+ t .Logf ("Custom registry available at: %s" , customRegistryURL )
650+
651+ // Ensure no models exist initially
652+ models , err := listModels (false , env .client , true , false , "" )
653+ require .NoError (t , err )
654+ if len (models ) != 0 {
655+ t .Fatal ("Expected no initial models, but found some" )
656+ }
657+
658+ // Create and push a test model with default org (ai/tag-test:latest)
659+ modelRef := "ai/tag-test:latest"
660+ modelID , hostFQDN , networkFQDN , digest := createAndPushTestModel (t , env .registryURL , modelRef , 2048 )
661+ t .Logf ("Test model pushed: %s (ID: %s) FQDN: %s Digest: %s" , hostFQDN , modelID , networkFQDN , digest )
662+
663+ // Pull the model using a simple reference
664+ pullRef := "tag-test"
665+ t .Logf ("Pulling model with reference: %s" , pullRef )
666+ err = pullModel (newPullCmd (), env .client , pullRef , true )
667+ require .NoError (t , err , "Failed to pull model" )
668+
669+ // Verify the model was pulled
670+ models , err = listModels (false , env .client , true , false , "" )
671+ require .NoError (t , err )
672+ truncatedID := modelID [7 :19 ]
673+ require .Equal (t , truncatedID , strings .TrimSpace (models ), "Model not found after pull" )
674+
675+ // Test 1: Push to default registry with various reference formats
676+ t .Run ("push to default registry" , func (t * testing.T ) {
677+ // Generate test cases for pushing to default registry
678+ pushInfo := modelInfo {
679+ name : "push-test" ,
680+ org : "ai" ,
681+ tag : "v1" ,
682+ registry : "registry.local:5000" ,
683+ modelID : modelID ,
684+ digest : digest ,
685+ expectedName : "ai/push-test:v1" ,
686+ }
687+ testCases := generateReferenceTestCases (pushInfo )
688+
689+ for _ , tc := range testCases {
690+ // Skip ID-based or digest-based references for push tests
691+ if strings .Contains (tc .name , "model ID" ) || strings .Contains (tc .name , "digest" ) {
692+ continue
693+ }
694+
695+ t .Run (tc .name , func (t * testing.T ) {
696+ // First tag the model with the custom registry reference
697+ t .Logf ("Tagging %s as %s" , "tag-test" , tc .ref )
698+ err := tagModel (newTagCmd (), env .client , "tag-test" , tc .ref )
699+ require .NoError (t , err , "Failed to tag model for custom registry" )
700+
701+ // Push the tagged model
702+ t .Logf ("Pushing model to custom registry with reference: %s" , tc .ref )
703+ _ , _ , err = env .client .Push (tc .ref , func (msg string ) {
704+ t .Logf ("Progress: %s" , msg )
705+ })
706+ require .NoError (t , err , "Failed to push model to custom registry" )
707+ t .Logf ("✓ Successfully pushed model to custom registry: %s" , tc .ref )
708+ })
709+ }
710+ })
711+
712+ // Test 2: Push to custom registry with explicit registry in reference
713+ t .Run ("push to custom registry" , func (t * testing.T ) {
714+ customTestCases := []struct {
715+ name string
716+ sourceRef string
717+ targetRef string
718+ }{
719+ {
720+ name : "push with custom registry and org" ,
721+ sourceRef : "tag-test" ,
722+ targetRef : "custom-registry.local:5000/ai/push-test:custom" ,
723+ },
724+ {
725+ name : "push with custom registry and different org" ,
726+ sourceRef : "tag-test" ,
727+ targetRef : "custom-registry.local:5000/test/push-test:v2" ,
728+ },
729+ {
730+ name : "push with custom registry FQDN" ,
731+ sourceRef : "tag-test" ,
732+ targetRef : "custom-registry.local:5000/custom/push-test:latest" ,
733+ },
734+ }
735+
736+ for _ , tc := range customTestCases {
737+ t .Run (tc .name , func (t * testing.T ) {
738+ // First tag the model with the custom registry reference
739+ t .Logf ("Tagging %s as %s" , tc .sourceRef , tc .targetRef )
740+ err := tagModel (newTagCmd (), env .client , tc .sourceRef , tc .targetRef )
741+ require .NoError (t , err , "Failed to tag model for custom registry" )
742+
743+ // Push the tagged model
744+ t .Logf ("Pushing model to custom registry with reference: %s" , tc .targetRef )
745+ _ , _ , err = env .client .Push (tc .targetRef , func (msg string ) {
746+ t .Logf ("Progress: %s" , msg )
747+ })
748+ require .NoError (t , err , "Failed to push model to custom registry" )
749+ t .Logf ("✓ Successfully pushed model to custom registry: %s" , tc .targetRef )
750+ })
751+ }
752+ })
753+
754+ // Test 3: Error cases
755+ t .Run ("error cases" , func (t * testing.T ) {
756+ t .Run ("push non-existent model" , func (t * testing.T ) {
757+ _ , _ , err := env .client .Push ("non-existent-model:v1" , func (msg string ) {})
758+ require .Error (t , err , "Should fail when pushing non-existent model" )
759+ t .Logf ("✓ Correctly failed to push non-existent model: %v" , err )
760+ })
761+
762+ t .Run ("push with invalid reference" , func (t * testing.T ) {
763+ _ , _ , err := env .client .Push ("" , func (msg string ) {})
764+ require .Error (t , err , "Should fail with empty reference" )
765+ t .Logf ("✓ Correctly failed to push with invalid reference: %v" , err )
766+ })
767+ })
768+
769+ // Final cleanup: remove the model
770+ t .Logf ("Removing model %s" , truncatedID )
771+ err = removeModel (env .client , modelID )
772+ require .NoError (t , err , "Failed to remove model" )
773+
774+ // Verify model was removed
775+ models , err = listModels (false , env .client , true , false , "" )
776+ require .NoError (t , err )
777+ require .Empty (t , strings .TrimSpace (models ), "Model should be removed" )
778+ }
0 commit comments