hunk.code_window: export function ThemeManager(StateManager) {
     return {
       setTheme,
       defaultTheme,
     };
     function setTheme(theme) {
       if (!theme) {
  
hunk.labels: keep keep keep add keep keep keep keep keep
hunk.after_edit:     autoTheme,
  
hunk.file_path: app/portainer/services/themeManager.js
hunk.type: add
file: package git
  
  import (
  	"context"
  	"net/http"
  	"net/http/httptest"
  	"net/url"
  	"testing"
  
  	"github.com/stretchr/testify/assert"
  )
  
  func Test_buildDownloadUrl(t *testing.T) {
  	a := NewAzureDownloader(nil)
  	u, err := a.buildDownloadUrl(&azureOptions{
  		organisation: "organisation",
  		project:      "project",
  		repository:   "repository",
  	}, "refs/heads/main")
  
  	expectedUrl, _ := url.Parse("https://dev.azure.com/organisation/project/_apis/git/repositories/repository/items?scopePath=/&download=true&versionDescriptor.version=main&$format=zip&recursionLevel=full&api-version=6.0&versionDescriptor.versionType=branch")
  	actualUrl, _ := url.Parse(u)
  	if assert.NoError(t, err) {
  		assert.Equal(t, expectedUrl.Host, actualUrl.Host)
  		assert.Equal(t, expectedUrl.Scheme, actualUrl.Scheme)
  		assert.Equal(t, expectedUrl.Path, actualUrl.Path)
  		assert.Equal(t, expectedUrl.Query(), actualUrl.Query())
  	}
  }
  
  func Test_buildRefsUrl(t *testing.T) {
  	a := NewAzureDownloader(nil)
  	u, err := a.buildRefsUrl(&azureOptions{
  		organisation: "organisation",
  		project:      "project",
  		repository:   "repository",
  	}, "refs/heads/main")
  
  	expectedUrl, _ := url.Parse("https://dev.azure.com/organisation/project/_apis/git/repositories/repository/refs?filterContains=main&api-version=6.0")
  	actualUrl, _ := url.Parse(u)
  	assert.NoError(t, err)
  	assert.Equal(t, expectedUrl.Host, actualUrl.Host)
  	assert.Equal(t, expectedUrl.Scheme, actualUrl.Scheme)
  	assert.Equal(t, expectedUrl.Path, actualUrl.Path)
  	assert.Equal(t, expectedUrl.Query(), actualUrl.Query())
  }
  
  func Test_parseAzureUrl(t *testing.T) {
  	type args struct {
  		url string
  	}
  	tests := []struct {
  		name    string
  		args    args
  		want    *azureOptions
  		wantErr bool
  	}{
  		{
  			name: "Expected SSH URL format starting with ssh://",
  			args: args{
  				url: "ssh://git@ssh.dev.azure.com:v3/Organisation/Project/Repository",
  			},
  			want: &azureOptions{
  				organisation: "Organisation",
  				project:      "Project",
  				repository:   "Repository",
  			},
  			wantErr: false,
  		},
  		{
  			name: "Expected SSH URL format starting with git@ssh",
  			args: args{
  				url: "git@ssh.dev.azure.com:v3/Organisation/Project/Repository",
  			},
  			want: &azureOptions{
  				organisation: "Organisation",
  				project:      "Project",
  				repository:   "Repository",
  			},
  			wantErr: false,
  		},
  		{
  			name: "Unexpected SSH URL format",
  			args: args{
  				url: "git@ssh.dev.azure.com:v3/Organisation/Repository",
  			},
  			wantErr: true,
  		},
  		{
  			name: "Expected HTTPS URL format",
  			args: args{
  				url: "https://Organisation@dev.azure.com/Organisation/Project/_git/Repository",
  			},
  			want: &azureOptions{
  				organisation: "Organisation",
  				project:      "Project",
  				repository:   "Repository",
  				username:     "Organisation",
  			},
  			wantErr: false,
  		},
  		{
  			name: "HTTPS URL with credentials",
  			args: args{
  				url: "https://username:password@dev.azure.com/Organisation/Project/_git/Repository",
  			},
  			want: &azureOptions{
  				organisation: "Organisation",
  				project:      "Project",
  				repository:   "Repository",
  				username:     "username",
  				password:     "password",
  			},
  			wantErr: false,
  		},
  		{
  			name: "HTTPS URL with password",
  			args: args{
  				url: "https://:password@dev.azure.com/Organisation/Project/_git/Repository",
  			},
  			want: &azureOptions{
  				organisation: "Organisation",
  				project:      "Project",
  				repository:   "Repository",
  				password:     "password",
  			},
  			wantErr: false,
  		},
  		{
  			name: "Visual Studio HTTPS URL with credentials",
  			args: args{
  				url: "https://username:password@organisation.visualstudio.com/project/_git/repository",
  			},
  			want: &azureOptions{
  				organisation: "organisation",
  				project:      "project",
  				repository:   "repository",
  				username:     "username",
  				password:     "password",
  			},
  			wantErr: false,
  		},
  		{
  			name: "Unexpected HTTPS URL format",
  			args: args{
  				url: "https://Organisation@dev.azure.com/Project/_git/Repository",
  			},
  			wantErr: true,
  		},
  	}
  	for _, tt := range tests {
  		t.Run(tt.name, func(t *testing.T) {
  			got, err := parseUrl(tt.args.url)
  			if (err != nil) != tt.wantErr {
  				t.Errorf("parseUrl() error = %v, wantErr %v", err, tt.wantErr)
  				return
  			}
  			assert.Equal(t, tt.want, got)
  		})
  	}
  }
  
  func Test_isAzureUrl(t *testing.T) {
  	type args struct {
  		s string
  	}
  	tests := []struct {
  		name string
  		args args
  		want bool
  	}{
  		{
  			name: "Is Azure url",
  			args: args{
  				s: "https://Organisation@dev.azure.com/Organisation/Project/_git/Repository",
  			},
  			want: true,
  		},
  		{
  			name: "Is Azure url",
  			args: args{
  				s: "https://portainer.visualstudio.com/project/_git/repository",
  			},
  			want: true,
  		},
  		{
  			name: "Is NOT Azure url",
  			args: args{
  				s: "https://github.com/Organisation/Repository",
  			},
  			want: false,
  		},
  	}
  	for _, tt := range tests {
  		t.Run(tt.name, func(t *testing.T) {
  			assert.Equal(t, tt.want, isAzureUrl(tt.args.s))
  		})
  	}
  }
  
  func Test_azureDownloader_downloadZipFromAzureDevOps(t *testing.T) {
  	type args struct {
  		options cloneOptions
  	}
  	type basicAuth struct {
  		username, password string
  	}
  	tests := []struct {
  		name string
  		args args
  		want *basicAuth
  	}{
  		{
  			name: "username, password embedded",
  			args: args{
  				options: cloneOptions{
  					repositoryUrl: "https://username:password@dev.azure.com/Organisation/Project/_git/Repository",
  				},
  			},
  			want: &basicAuth{
  				username: "username",
  				password: "password",
  			},
  		},
  		{
  			name: "username, password embedded, clone options take precedence",
  			args: args{
  				options: cloneOptions{
  					repositoryUrl: "https://username:password@dev.azure.com/Organisation/Project/_git/Repository",
  					username:      "u",
  					password:      "p",
  				},
  			},
  			want: &basicAuth{
  				username: "u",
  				password: "p",
  			},
  		},
  		{
  			name: "no credentials",
  			args: args{
  				options: cloneOptions{
  					repositoryUrl: "https://dev.azure.com/Organisation/Project/_git/Repository",
  				},
  			},
  		},
  	}
  	for _, tt := range tests {
  		t.Run(tt.name, func(t *testing.T) {
  			var zipRequestAuth *basicAuth
  			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  				if username, password, ok := r.BasicAuth(); ok {
  					zipRequestAuth = &basicAuth{username, password}
  				}
  				w.WriteHeader(http.StatusNotFound) // this makes function under test to return an error
  			}))
  			defer server.Close()
  
  			a := &azureDownloader{
  				client:  server.Client(),
  				baseUrl: server.URL,
  			}
  			_, err := a.downloadZipFromAzureDevOps(context.Background(), tt.args.options)
  			assert.Error(t, err)
  			assert.Equal(t, tt.want, zipRequestAuth)
  		})
  	}
  }
  
  func Test_azureDownloader_latestCommitID(t *testing.T) {
  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  		response := `{
  			"value": [
  				{
  					"name": "refs/heads/feature/calcApp",
  					"objectId": "ffe9cba521f00d7f60e322845072238635edb451",
  					"creator": {
  						"displayName": "Normal Paulk",
  						"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/ac5aaba6-a66a-4e1d-b508-b060ec624fa9",
  						"_links": {
  							"avatar": {
  								"href": "https://dev.azure.com/fabrikam/_apis/GraphProfile/MemberAvatars/aad.YmFjMGYyZDctNDA3ZC03OGRhLTlhMjUtNmJhZjUwMWFjY2U5"
  							}
  						},
  						"id": "ac5aaba6-a66a-4e1d-b508-b060ec624fa9",
  						"uniqueName": "dev@mailserver.com",
  						"imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=ac5aaba6-a66a-4e1d-b508-b060ec624fa9",
  						"descriptor": "aad.YmFjMGYyZDctNDA3ZC03OGRhLTlhMjUtNmJhZjUwMWFjY2U5"
  					},
  					"url": "https://dev.azure.com/fabrikam/7484f783-66a3-4f27-b7cd-6b08b0b077ed/_apis/git/repositories/d3d1760b-311c-4175-a726-20dfc6a7f885/refs?filter=heads%2Ffeature%2FcalcApp"
  				},
  				{
  					"name": "refs/heads/feature/replacer",
  					"objectId": "917131a709996c5cfe188c3b57e9a6ad90e8b85c",
  					"creator": {
  						"displayName": "Normal Paulk",
  						"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/ac5aaba6-a66a-4e1d-b508-b060ec624fa9",
  						"_links": {
  							"avatar": {
  								"href": "https://dev.azure.com/fabrikam/_apis/GraphProfile/MemberAvatars/aad.YmFjMGYyZDctNDA3ZC03OGRhLTlhMjUtNmJhZjUwMWFjY2U5"
  							}
  						},
  						"id": "ac5aaba6-a66a-4e1d-b508-b060ec624fa9",
  						"uniqueName": "dev@mailserver.com",
  						"imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=ac5aaba6-a66a-4e1d-b508-b060ec624fa9",
  						"descriptor": "aad.YmFjMGYyZDctNDA3ZC03OGRhLTlhMjUtNmJhZjUwMWFjY2U5"
  					},
  					"url": "https://dev.azure.com/fabrikam/7484f783-66a3-4f27-b7cd-6b08b0b077ed/_apis/git/repositories/d3d1760b-311c-4175-a726-20dfc6a7f885/refs?filter=heads%2Ffeature%2Freplacer"
  				},
  				{
  					"name": "refs/heads/master",
  					"objectId": "ffe9cba521f00d7f60e322845072238635edb451",
  					"creator": {
  						"displayName": "Normal Paulk",
  						"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/ac5aaba6-a66a-4e1d-b508-b060ec624fa9",
  						"_links": {
  							"avatar": {
  								"href": "https://dev.azure.com/fabrikam/_apis/GraphProfile/MemberAvatars/aad.YmFjMGYyZDctNDA3ZC03OGRhLTlhMjUtNmJhZjUwMWFjY2U5"
  							}
  						},
  						"id": "ac5aaba6-a66a-4e1d-b508-b060ec624fa9",
  						"uniqueName": "dev@mailserver.com",
  						"imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=ac5aaba6-a66a-4e1d-b508-b060ec624fa9",
  						"descriptor": "aad.YmFjMGYyZDctNDA3ZC03OGRhLTlhMjUtNmJhZjUwMWFjY2U5"
  					},
  					"url": "https://dev.azure.com/fabrikam/7484f783-66a3-4f27-b7cd-6b08b0b077ed/_apis/git/repositories/d3d1760b-311c-4175-a726-20dfc6a7f885/refs?filter=heads%2Fmaster"
  				}
  			],
  			"count": 3
  		}`
  		w.Header().Set("Content-Type", "application/json")
  		w.Write([]byte(response))
  	}))
  	defer server.Close()
  
  	a := &azureDownloader{
  		client:  server.Client(),
  		baseUrl: server.URL,
  	}
  
  	tests := []struct {
  		name    string
  		args    fetchOptions
  		want    string
  		wantErr bool
  	}{
  		{
  			name: "should be able to parse response",
  			args: fetchOptions{
  				referenceName: "refs/heads/master",
  				repositoryUrl: "https://dev.azure.com/Organisation/Project/_git/Repository"},
  			want:    "ffe9cba521f00d7f60e322845072238635edb451",
  			wantErr: false,
  		},
  		{
  			name: "should be able to parse response",
  			args: fetchOptions{
  				referenceName: "refs/heads/unknown",
  				repositoryUrl: "https://dev.azure.com/Organisation/Project/_git/Repository"},
  			want:    "",
  			wantErr: true,
  		},
  	}
  
  	for _, tt := range tests {
  		t.Run(tt.name, func(t *testing.T) {
  			id, err := a.latestCommitID(context.Background(), tt.args)
  			if (err != nil) != tt.wantErr {
  				t.Errorf("azureDownloader.latestCommitID() error = %v, wantErr %v", err, tt.wantErr)
  				return
  			}
  			assert.Equal(t, tt.want, id)
  		})
  	}
  }
  