/*
 *
 * Copyright (c) 2025 Bocloud Technologies Co., Ltd.
 * installer is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          <http://license.coscl.org.cn/MulanPSL2>
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 *
 */

package build

import (
	"fmt"
	"testing"

	"github.com/agiledragon/gomonkey/v2"
	"github.com/stretchr/testify/assert"
	"gopkg.openfuyao.cn/bkeadm/pkg/executor/exec"

	"gopkg.openfuyao.cn/bkeadm/pkg/executor/docker"
	"gopkg.openfuyao.cn/bkeadm/pkg/global"
	"gopkg.openfuyao.cn/bkeadm/pkg/server"
	"gopkg.openfuyao.cn/bkeadm/utils"
	"gopkg.openfuyao.cn/bkeadm/utils/log"
)

const (
	testChannelBufferOne     = 1
	testChannelBufferHundred = 100
)

func TestBuildRegistry(t *testing.T) {
	arch := []string{"amd64"}

	tests := []struct {
		name        string
		source      string
		mockPull    func(*docker.Client, docker.ImageRef, utils.RetryOptions) error
		mockTag     func(*docker.Client, string, string) error
		mockSave    func(*docker.Client, string, string) error
		mockRemove  func(*docker.Client, docker.ImageRef) error
		expectError bool
	}{
		{
			name:        "successful build",
			source:      "test-image:latest",
			mockPull:    func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error { return nil },
			mockTag:     func(c *docker.Client, source, target string) error { return nil },
			mockSave:    func(c *docker.Client, source, target string) error { return nil },
			mockRemove:  func(c *docker.Client, ref docker.ImageRef) error { return nil },
			expectError: false,
		},
		{
			name:   "pull fails",
			source: "test-image:latest",
			mockPull: func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error {
				return fmt.Errorf("pull error")
			},
			expectError: true,
		},
		{
			name:        "tag fails",
			source:      "test-image:latest",
			mockPull:    func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error { return nil },
			mockTag:     func(c *docker.Client, source, target string) error { return fmt.Errorf("tag error") },
			expectError: true,
		},
		{
			name:        "save fails",
			source:      "test-image:latest",
			mockPull:    func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error { return nil },
			mockTag:     func(c *docker.Client, source, target string) error { return nil },
			mockSave:    func(c *docker.Client, source, target string) error { return fmt.Errorf("save error") },
			expectError: true,
		},
		{
			name:        "remove fails",
			source:      "test-image:latest",
			mockPull:    func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error { return nil },
			mockTag:     func(c *docker.Client, source, target string) error { return nil },
			mockSave:    func(c *docker.Client, source, target string) error { return nil },
			mockRemove:  func(c *docker.Client, ref docker.ImageRef) error { return fmt.Errorf("remove error") },
			expectError: true,
		},
	}

	if global.Docker == nil {
		global.Docker = &docker.Client{}
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Apply patches
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc((*docker.Client).Pull, tt.mockPull)

			patches.ApplyFunc((*docker.Client).Tag, tt.mockTag)

			patches.ApplyFunc((*docker.Client).Save, tt.mockSave)

			patches.ApplyFunc((*docker.Client).Remove, tt.mockRemove)

			patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})

			// Mock bke and utils.ImageFile
			patches.ApplyGlobalVar(&bke, "/tmp/bke")

			// Reset needRemoveImage for each test
			originalNeedRemoveImage := needRemoveImage
			needRemoveImage = []string{}
			defer func() {
				needRemoveImage = originalNeedRemoveImage
			}()

			err := buildRegistry(tt.source, arch)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.Contains(t, needRemoveImage, tt.source)
			}
		})
	}
}

func TestSyncImageTag(t *testing.T) {
	cr := Repo{
		Architecture: []string{"amd64"},
	}
	subImage := SubImage{
		SourceRepo: "source-repo",
		TargetRepo: "target-repo",
	}
	image := Image{
		Name: "test-image",
		Tag:  []string{"v1.0.0"},
	}

	// Create a channel for testing
	imageChan := make(chan docker.ImageRef, testChannelBufferOne)
	defer close(imageChan)

	tests := []struct {
		name           string
		imageTag       string
		mockImageTrack func(string, string, string, string, []string) (string, error)
		mockSyncImage  func(string, string, []string, chan<- docker.ImageRef) error
		expectError    bool
	}{
		{
			name:     "successful sync",
			imageTag: "v1.0.0",
			mockImageTrack: func(sourceRepo, imageTrack, imageName, tag string, arch []string) (string, error) {
				return "source:image", nil
			},
			mockSyncImage: func(source, target string, arch []string, imageChan chan<- docker.ImageRef) error {
				return nil
			},
			expectError: false,
		},
		{
			name:     "image track fails",
			imageTag: "v1.0.0",
			mockImageTrack: func(sourceRepo, imageTrack, imageName, tag string, arch []string) (string, error) {
				return "", fmt.Errorf("track error")
			},
			expectError: true,
		},
		{
			name:     "sync image fails",
			imageTag: "v1.0.0",
			mockImageTrack: func(sourceRepo, imageTrack, imageName, tag string, arch []string) (string, error) {
				return "source:image", nil
			},
			mockSyncImage: func(source, target string, arch []string, imageChan chan<- docker.ImageRef) error {
				return fmt.Errorf("sync error")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Apply patches
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(imageTrack, tt.mockImageTrack)

			patches.ApplyFunc(syncImage, tt.mockSyncImage)

			err := syncImageTag(subImage, image, tt.imageTag, cr, imageChan)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestCollectRepo(t *testing.T) {
	stopChan := make(chan struct{})
	defer close(stopChan)

	cfg := &BuildConfig{
		Registry: registry{
			ImageAddress: "registry.example.com",
			Architecture: []string{"amd64"},
		},
		Repos: []Repo{
			{
				NeedDownload: true,
				SubImages: []SubImage{
					{
						Images: []Image{
							{
								Name: "test-image",
								Tag:  []string{"v1.0.0"},
							},
						},
					},
				},
			},
		},
	}

	tests := []struct {
		name                    string
		mockRemoveImageRegistry func(string) error
		mockStartImageRegistry  func(string, string, string, string) error
		mockSyncAllRepoImages   func(*BuildConfig, *syncChannels) error
		mockPackImageAndCleanup func() error
		expectError             bool
	}{
		{
			name:                    "successful collection",
			mockRemoveImageRegistry: func(name string) error { return nil },
			mockStartImageRegistry:  func(name, image, port, dir string) error { return nil },
			mockSyncAllRepoImages:   func(cfg *BuildConfig, channels *syncChannels) error { return nil },
			mockPackImageAndCleanup: func() error { return nil },
			expectError:             false,
		},
		{
			name:                    "start image registry fails",
			mockRemoveImageRegistry: func(name string) error { return nil },
			mockStartImageRegistry:  func(name, image, port, dir string) error { return fmt.Errorf("start error") },
			expectError:             true,
		},
		{
			name:                    "sync all repo images fails",
			mockRemoveImageRegistry: func(name string) error { return nil },
			mockStartImageRegistry:  func(name, image, port, dir string) error { return nil },
			mockSyncAllRepoImages:   func(cfg *BuildConfig, channels *syncChannels) error { return fmt.Errorf("sync error") },
			expectError:             true,
		},
		{
			name:                    "pack image and cleanup fails",
			mockRemoveImageRegistry: func(name string) error { return nil },
			mockStartImageRegistry:  func(name, image, port, dir string) error { return nil },
			mockSyncAllRepoImages:   func(cfg *BuildConfig, channels *syncChannels) error { return nil },
			mockPackImageAndCleanup: func() error { return fmt.Errorf("pack error") },
			expectError:             true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Apply patches
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(server.RemoveImageRegistry, tt.mockRemoveImageRegistry)

			patches.ApplyFunc(server.StartImageRegistry, tt.mockStartImageRegistry)

			patches.ApplyFunc(syncAllRepoImages, tt.mockSyncAllRepoImages)

			patches.ApplyFunc(packImageAndCleanup, tt.mockPackImageAndCleanup)

			patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})

			// Mock tmpRegistry
			patches.ApplyGlobalVar(&tmpRegistry, "/tmp/registry")

			err := collectRepo(cfg, stopChan)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestSyncRepoImageTags(t *testing.T) {
	stopChan := make(chan struct{})
	defer close(stopChan)

	internalStopChan := make(chan struct{})
	pullCompleteChan := make(chan struct{})
	imageChan := make(chan docker.ImageRef, testChannelBufferHundred)
	defer close(imageChan)

	channels := &syncChannels{
		stopChan:         stopChan,
		internalStopChan: internalStopChan,
		pullCompleteChan: pullCompleteChan,
		imageChan:        imageChan,
	}

	cr := Repo{
		Architecture: []string{"amd64"},
	}
	subImage := SubImage{
		Images: []Image{
			{
				Name: "test-image",
				Tag:  []string{"v1.0.0", "v1.0.1"},
			},
		},
	}

	tests := []struct {
		name             string
		mockSyncImageTag func(SubImage, Image, string, Repo, chan<- docker.ImageRef) error
		expectError      bool
	}{
		{
			name: "successful sync",
			mockSyncImageTag: func(subImage SubImage, image Image, imageTag string, cr Repo, imageChan chan<- docker.ImageRef) error {
				return nil
			},
			expectError: false,
		},
		{
			name: "sync image tag fails",
			mockSyncImageTag: func(subImage SubImage, image Image, imageTag string, cr Repo, imageChan chan<- docker.ImageRef) error {
				return fmt.Errorf("sync error")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Apply patches
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(syncImageTag, tt.mockSyncImageTag)

			patches.ApplyFunc(closeChanStruct, func(ch chan struct{}) {})

			patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})

			err := syncRepoImageTags(cr, subImage, channels)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestSyncAllRepoImages(t *testing.T) {
	cfg := &BuildConfig{
		Repos: []Repo{
			{
				NeedDownload: true,
				SubImages: []SubImage{
					{
						Images: []Image{
							{
								Name: "test-image",
								Tag:  []string{"v1.0.0"},
							},
						},
					},
				},
			},
		},
	}

	stopChan := make(chan struct{})
	internalStopChan := make(chan struct{})
	pullCompleteChan := make(chan struct{})
	imageChan := make(chan docker.ImageRef, testChannelBufferHundred)
	defer close(imageChan)

	channels := &syncChannels{
		stopChan:         stopChan,
		internalStopChan: internalStopChan,
		pullCompleteChan: pullCompleteChan,
		imageChan:        imageChan,
	}

	tests := []struct {
		name                  string
		mockSyncRepoImageTags func(Repo, SubImage, *syncChannels) error
		expectError           bool
	}{
		{
			name: "successful sync",
			mockSyncRepoImageTags: func(cr Repo, subImage SubImage, channels *syncChannels) error {
				return nil
			},
			expectError: false,
		},
		{
			name: "sync repo image tags fails",
			mockSyncRepoImageTags: func(cr Repo, subImage SubImage, channels *syncChannels) error {
				return fmt.Errorf("sync error")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Apply patches
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(syncRepoImageTags, tt.mockSyncRepoImageTags)

			err := syncAllRepoImages(cfg, channels)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestPullAndTagSingleArchImage(t *testing.T) {
	tests := []struct {
		name        string
		source      string
		target      string
		arch        string
		mockPull    func(*docker.Client, docker.ImageRef, utils.RetryOptions) error
		mockTag     func(*docker.Client, string, string) error
		expectError bool
	}{
		{
			name:        "successful pull and tag",
			source:      "source:image",
			target:      "target:image",
			arch:        "amd64",
			mockPull:    func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error { return nil },
			mockTag:     func(c *docker.Client, source, target string) error { return nil },
			expectError: false,
		},
		{
			name:   "pull fails",
			source: "source:image",
			target: "target:image",
			arch:   "amd64",
			mockPull: func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error {
				return fmt.Errorf("pull error")
			},
			expectError: true,
		},
		{
			name:        "tag fails",
			source:      "source:image",
			target:      "target:image",
			arch:        "amd64",
			mockPull:    func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error { return nil },
			mockTag:     func(c *docker.Client, source, target string) error { return fmt.Errorf("tag error") },
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Apply patches
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc((*docker.Client).Pull, tt.mockPull)

			patches.ApplyFunc((*docker.Client).Tag, tt.mockTag)

			patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})

			// Reset needRemoveImage for each test
			originalNeedRemoveImage := needRemoveImage
			needRemoveImage = []string{}
			defer func() {
				needRemoveImage = originalNeedRemoveImage
			}()

			err := pullAndTagSingleArchImage(tt.source, tt.target, tt.arch)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.Contains(t, needRemoveImage, tt.source)
				assert.Contains(t, needRemoveImage, tt.target)
			}
		})
	}
}

func TestPullAndPushMultiArchImage(t *testing.T) {
	arch := []string{"amd64", "arm64"}

	tests := []struct {
		name        string
		source      string
		target      string
		mockPull    func(*docker.Client, docker.ImageRef, utils.RetryOptions) error
		mockTag     func(*docker.Client, string, string) error
		mockRemove  func(*docker.Client, docker.ImageRef) error
		mockPush    func(*docker.Client, docker.ImageRef) error
		expectError bool
	}{
		{
			name:        "successful pull and push",
			source:      "source:image",
			target:      "target:image",
			mockPull:    func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error { return nil },
			mockTag:     func(c *docker.Client, source, target string) error { return nil },
			mockRemove:  func(c *docker.Client, ref docker.ImageRef) error { return nil },
			mockPush:    func(c *docker.Client, ref docker.ImageRef) error { return nil },
			expectError: false,
		},
		{
			name:   "pull fails",
			source: "source:image",
			target: "target:image",
			mockPull: func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error {
				return fmt.Errorf("pull error")
			},
			expectError: true,
		},
		{
			name:        "tag fails",
			source:      "source:image",
			target:      "target:image",
			mockPull:    func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error { return nil },
			mockTag:     func(c *docker.Client, source, target string) error { return fmt.Errorf("tag error") },
			expectError: true,
		},
		{
			name:        "remove fails",
			source:      "source:image",
			target:      "target:image",
			mockPull:    func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error { return nil },
			mockTag:     func(c *docker.Client, source, target string) error { return nil },
			mockRemove:  func(c *docker.Client, ref docker.ImageRef) error { return fmt.Errorf("remove error") },
			expectError: true,
		},
		{
			name:        "push fails",
			source:      "source:image",
			target:      "target:image",
			mockPull:    func(c *docker.Client, ref docker.ImageRef, opts utils.RetryOptions) error { return nil },
			mockTag:     func(c *docker.Client, source, target string) error { return nil },
			mockRemove:  func(c *docker.Client, ref docker.ImageRef) error { return nil },
			mockPush:    func(c *docker.Client, ref docker.ImageRef) error { return fmt.Errorf("push error") },
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Apply patches
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc((*docker.Client).Pull, tt.mockPull)

			patches.ApplyFunc((*docker.Client).Tag, tt.mockTag)

			patches.ApplyFunc((*docker.Client).Remove, tt.mockRemove)

			patches.ApplyFunc((*docker.Client).Push, tt.mockPush)

			patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})

			patches.ApplyFunc(log.Infof, func(format string, args ...interface{}) {})

			// Reset needRemoveImage for each test
			originalNeedRemoveImage := needRemoveImage
			needRemoveImage = []string{}
			defer func() {
				needRemoveImage = originalNeedRemoveImage
			}()

			manifestCreateCmd, manifestAnnotate, err := pullAndPushMultiArchImage(tt.source, tt.target, arch)

			if tt.expectError {
				assert.Error(t, err)
				assert.Empty(t, manifestCreateCmd)
				assert.Empty(t, manifestAnnotate)
			} else {
				assert.NoError(t, err)
				assert.NotEmpty(t, manifestCreateCmd)
				assert.NotEmpty(t, manifestAnnotate)
			}
		})
	}
}

func TestExecuteManifestCommands(t *testing.T) {
	manifestCreateCmd := "docker manifest create --insecure target:image source1 source2"
	manifestAnnotate := []string{
		"docker manifest annotate target:image --os linux --arch amd64 source1",
		"docker manifest annotate target:image --os linux --arch arm64 source2",
	}

	tests := []struct {
		name               string
		mockExecuteCommand func(*exec.CommandExecutor, []string, string, ...string) error
		expectError        bool
	}{
		{
			name:               "successful execution",
			mockExecuteCommand: func(executor *exec.CommandExecutor, env []string, command string, args ...string) error { return nil },
			expectError:        false,
		},
		{
			name: "execute command fails",
			mockExecuteCommand: func(executor *exec.CommandExecutor, env []string, command string, args ...string) error {
				return fmt.Errorf("exec error")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Apply patches
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc((*exec.CommandExecutor).ExecuteCommandWithEnv, tt.mockExecuteCommand)

			patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})

			patches.ApplyFunc(log.Infof, func(format string, args ...interface{}) {})

			err := global.ExecuteManifestCommands("target:image", manifestCreateCmd, manifestAnnotate)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestSyncImage(t *testing.T) {
	// Create a channel for testing
	imageChan := make(chan docker.ImageRef, testOneValue)
	defer close(imageChan)

	tests := []struct {
		name                          string
		source                        string
		target                        string
		arch                          []string
		mockPullAndTagSingleArchImage func(string, string, string) error
		mockPullAndPushMultiArchImage func(string, string, []string) (string, []string, error)
		mockExecuteManifestCommands   func(string, string, []string) error
		expectError                   bool
	}{
		{
			name:                          "single arch successful sync",
			source:                        "source:image",
			target:                        "target:image",
			arch:                          []string{"amd64"},
			mockPullAndTagSingleArchImage: func(source, target, arch string) error { return nil },
			expectError:                   false,
		},
		{
			name:   "multi arch successful sync",
			source: "source:image",
			target: "target:image",
			arch:   []string{"amd64", "arm64"},
			mockPullAndPushMultiArchImage: func(source, target string, arch []string) (string, []string, error) {
				return "manifest cmd", []string{"annotate cmd"}, nil
			},
			mockExecuteManifestCommands: func(target, manifestCreateCmd string, manifestAnnotate []string) error {
				return nil
			},
			expectError: false,
		},
		{
			name:                          "single arch sync fails",
			source:                        "source:image",
			target:                        "target:image",
			arch:                          []string{"amd64"},
			mockPullAndTagSingleArchImage: func(source, target, arch string) error { return fmt.Errorf("pull error") },
			expectError:                   true,
		},
		{
			name:   "multi arch pull fails",
			source: "source:image",
			target: "target:image",
			arch:   []string{"amd64", "arm64"},
			mockPullAndPushMultiArchImage: func(source, target string, arch []string) (string, []string, error) {
				return "", nil, fmt.Errorf("pull error")
			},
			expectError: true,
		},
		{
			name:   "multi arch execute fails",
			source: "source:image",
			target: "target:image",
			arch:   []string{"amd64", "arm64"},
			mockPullAndPushMultiArchImage: func(source, target string, arch []string) (string, []string, error) {
				return "manifest cmd", []string{"annotate cmd"}, nil
			},
			mockExecuteManifestCommands: func(target, manifestCreateCmd string, manifestAnnotate []string) error {
				return fmt.Errorf("exec error")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			patches := gomonkey.NewPatches()
			defer patches.Reset()

			if len(tt.arch) == testOneValue {
				patches.ApplyFunc(pullAndTagSingleArchImage, tt.mockPullAndTagSingleArchImage)
			} else {
				patches.ApplyFunc(pullAndPushMultiArchImage, tt.mockPullAndPushMultiArchImage)
				patches.ApplyFunc(global.ExecuteManifestCommands, tt.mockExecuteManifestCommands)
			}

			patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})

			err := syncImage(tt.source, tt.target, tt.arch, imageChan)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestCleanBuildImage(t *testing.T) {
	// Set up needRemoveImage with some test data
	originalNeedRemoveImage := needRemoveImage
	needRemoveImage = []string{"image1", "image2", "image3"}
	defer func() {
		needRemoveImage = originalNeedRemoveImage
	}()

	// Apply patches
	patches := gomonkey.NewPatches()
	defer patches.Reset()
	patches.ApplyFunc((*docker.Client).Remove, func(c *docker.Client, ref docker.ImageRef) error {
		return nil
	})

	// Call the function
	cleanBuildImage()

	// The function should have attempted to remove all images in needRemoveImage
	// Since we can't easily track individual calls with gomonkey, we'll just verify
	// that the function runs without error
	assert.True(t, true) // This just ensures the function runs without panic
}

func TestCloseChanStruct(t *testing.T) {
	// Test with a channel that's not closed
	ch := make(chan struct{})

	// Verify channel is not closed initially
	patches := gomonkey.NewPatches()
	defer patches.Reset()
	patches.ApplyFunc(utils.IsChanClosed, func(ch interface{}) bool {
		return false
	})

	closeChanStruct(ch)

	// Channel should be closed now
	assert.True(t, true) // This just ensures the function runs without panic

	// Test with a channel that's already closed
	ch2 := make(chan struct{})
	close(ch2)

	patches.ApplyFunc(utils.IsChanClosed, func(ch interface{}) bool {
		return true
	})

	closeChanStruct(ch2)

	// Function should handle already closed channel gracefully
	assert.True(t, true) // This just ensures the function runs without panic
}

func TestPackImageAndCleanup(t *testing.T) {
	tests := []struct {
		name                    string
		mockTarGZ               func(prefix, target string) error
		mockRemoveImageRegistry func(string) error
		expectError             bool
	}{
		{
			name:                    "successful pack and cleanup",
			mockTarGZ:               func(src, dst string) error { return nil },
			mockRemoveImageRegistry: func(name string) error { return nil },
			expectError:             false,
		},
		{
			name:        "tar gz fails",
			mockTarGZ:   func(src, dst string) error { return fmt.Errorf("tar error") },
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Apply patches
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(global.TarGZ, tt.mockTarGZ)

			patches.ApplyFunc(server.RemoveImageRegistry, tt.mockRemoveImageRegistry)

			patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})

			// Mock bke and utils.ImageDataFile
			patches.ApplyGlobalVar(&bke, "/tmp/bke")

			err := packImageAndCleanup()

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestPushImageWithError(t *testing.T) {
	// Test the error case in pushImage

	imageChan := make(chan docker.ImageRef, testOneValue)
	pullCompleteChan := make(chan struct{})
	pushCompleteChan := make(chan string)
	stopChan := make(chan struct{})

	// Send an image to the channel
	imageChan <- docker.ImageRef{Image: "test-image", Platform: "amd64"}

	// Apply patches to simulate push error
	patches := gomonkey.NewPatches()
	defer patches.Reset()
	patches.ApplyFunc((*docker.Client).Push, func(c *docker.Client, ref docker.ImageRef) error {
		return fmt.Errorf("push error")
	})

	patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})

	patches.ApplyFunc(log.Infof, func(format string, args ...interface{}) {})

	// Run pushImage in a goroutine
	go func() {
		pushImage(imageChan, pullCompleteChan, pushCompleteChan, stopChan)
	}()

	// Close the image channel to stop the loop
	close(imageChan)

	// Wait for error signal
	result := <-pushCompleteChan

	// Verify the error result
	assert.Contains(t, result, "push error")
}

func TestSyncChannelsStruct(t *testing.T) {
	// Test that the syncChannels struct has the expected fields
	stopChan := make(<-chan struct{})
	internalStopChan := make(chan struct{})
	pullCompleteChan := make(chan struct{})
	imageChan := make(chan<- docker.ImageRef)

	channels := &syncChannels{
		stopChan:         stopChan,
		internalStopChan: internalStopChan,
		pullCompleteChan: pullCompleteChan,
		imageChan:        imageChan,
	}

	assert.Equal(t, stopChan, channels.stopChan)
	assert.Equal(t, internalStopChan, channels.internalStopChan)
	assert.Equal(t, pullCompleteChan, channels.pullCompleteChan)
	assert.Equal(t, imageChan, channels.imageChan)
}