package metrics

import (
	"context"
	"testing"

	nbdns "github.com/netbirdio/netbird/dns"
	"github.com/netbirdio/netbird/idp/dex"
	rpservice "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
	resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
	routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
	networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
	nbpeer "github.com/netbirdio/netbird/management/server/peer"
	"github.com/netbirdio/netbird/management/server/posture"
	"github.com/netbirdio/netbird/management/server/types"
	"github.com/netbirdio/netbird/route"
)

type mockDatasource struct{}

// GetAllConnectedPeers returns a map of connected peer IDs for use in tests with predefined information
func (mockDatasource) GetAllConnectedPeers() map[string]struct{} {
	return map[string]struct{}{
		"1": {},
	}
}

// GetAllAccounts returns a list of *server.Account for use in tests with predefined information
func (mockDatasource) GetAllAccounts(_ context.Context) []*types.Account {
	localUserID := dex.EncodeDexUserID("10", "local")
	idpUserID := dex.EncodeDexUserID("20", "zitadel-d5uv82dra0haedlf6kv0")
	oidcUserID := dex.EncodeDexUserID("30", "d6jvvp69kmnc73c9pl40")
	return []*types.Account{
		{
			Id:       "1",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: false,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1", Flags: nbpeer.Flags{ServerSSHAllowed: true, RosenpassEnabled: true}},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
					SourcePostureChecks: []string{"1"},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			PostureChecks: []*posture.Checks{
				{
					ID:   "1",
					Name: "test",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.1",
						},
					},
				},
				{
					ID:   "2",
					Name: "tes2",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.2",
						},
					},
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				localUserID: {
					Id:            localUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Services: []*rpservice.Service{
				{
					ID:      "svc1",
					Enabled: true,
					Targets: []*rpservice.Target{
						{TargetType: "peer"},
						{TargetType: "host"},
					},
					Auth: rpservice.AuthConfig{
						PasswordAuth: &rpservice.PasswordAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusActive)},
				},
				{
					ID:      "svc2",
					Enabled: false,
					Targets: []*rpservice.Target{
						{TargetType: "domain"},
					},
					Auth: rpservice.AuthConfig{
						BearerAuth: &rpservice.BearerAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusPending)},
				},
			},
		},
		{
			Id:       "2",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: true,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1"},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				idpUserID: {
					Id:            idpUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				oidcUserID: {
					Id:            oidcUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Networks: []*networkTypes.Network{
				{
					ID:        "1",
					AccountID: "1",
				},
			},
			NetworkResources: []*resourceTypes.NetworkResource{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
				{
					ID:        "2",
					AccountID: "1",
					NetworkID: "1",
				},
			},
			NetworkRouters: []*routerTypes.NetworkRouter{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
			},
		},
	}
}

// GetStoreEngine returns FileStoreEngine
func (mockDatasource) GetStoreEngine() types.Engine {
	return types.FileStoreEngine
}

// GetCustomDomainsCounts returns test custom domain counts.
func (mockDatasource) GetCustomDomainsCounts(_ context.Context) (int64, int64, error) {
	return 3, 2, nil
}

// TestGenerateProperties tests and validate the properties generation by using the mockDatasource for the Worker.generateProperties
func TestGenerateProperties(t *testing.T) {
	ds := mockDatasource{}
	worker := Worker{
		dataSource:  ds,
		connManager: ds,
		idpManager:  EmbeddedType,
	}

	properties := worker.generateProperties(context.Background())

	if properties["accounts"] != 2 {
		t.Errorf("expected 2 accounts, got %d", properties["accounts"])
	}
	if properties["peers"] != 2 {
		t.Errorf("expected 2 peers, got %d", properties["peers"])
	}
	if properties["routes"] != 2 {
		t.Errorf("expected 2 routes, got %d", properties["routes"])
	}
	if properties["networks"] != 1 {
		t.Errorf("expected 1 networks, got %d", properties["networks"])
	}
	if properties["network_resources"] != 2 {
		t.Errorf("expected 2 network_resources, got %d", properties["network_resources"])
	}
	if properties["network_routers"] != 1 {
		t.Errorf("expected 1 network_routers, got %d", properties["network_routers"])
	}
	if properties["rules"] != 4 {
		t.Errorf("expected 4 rules, got %d", properties["rules"])
	}
	if properties["users"] != 3 {
		t.Errorf("expected 3 users, got %d", properties["users"])
	}
	if properties["setup_keys_usage"] != 2 {
		t.Errorf("expected 1 setup_keys_usage, got %d", properties["setup_keys_usage"])
	}
	if properties["pats"] != 4 {
		t.Errorf("expected 4 personal_access_tokens, got %d", properties["pats"])
	}
	if properties["peers_ssh_enabled"] != 2 {
		t.Errorf("expected 2 peers_ssh_enabled, got %d", properties["peers_ssh_enabled"])
	}
	if properties["routes_with_routing_groups"] != 2 {
		t.Errorf("expected 2 routes_with_routing_groups, got %d", properties["routes_with_routing_groups"])
	}
	if properties["rules_protocol_tcp"] != 4 {
		t.Errorf("expected 4 rules_protocol_tcp, got %d", properties["rules_protocol_tcp"])
	}
	if properties["rules_direction_oneway"] != 2 {
		t.Errorf("expected 2 rules_direction_oneway, got %d", properties["rules_direction_oneway"])
	}

	if properties["active_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_peers_last_day, got %d", properties["active_peers_last_day"])
	}
	if properties["min_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 min_active_peer_version, got %s", properties["min_active_peer_version"])
	}
	if properties["max_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 max_active_peer_version, got %s", properties["max_active_peer_version"])
	}

	if properties["peers_login_expiration_enabled"] != 2 {
		t.Errorf("expected 2 peers_login_expiration_enabled, got %d", properties["peers_login_expiration_enabled"])
	}

	if properties["service_users"] != 2 {
		t.Errorf("expected 2 service_users, got %d", properties["service_users"])
	}

	if properties["peer_os_linux"] != 2 {
		t.Errorf("expected 2 peer_os_linux, got %d", properties["peer_os_linux"])
	}

	if properties["ephemeral_peers_setup_keys"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["ephemeral_peers_setup_keys_usage"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys_usage, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["nameservers"] != 2 {
		t.Errorf("expected 2 nameservers, got %d", properties["nameservers"])
	}

	if properties["groups"] != 4 {
		t.Errorf("expected 4 groups, got %d", properties["groups"])
	}

	if properties["user_peers"] != 2 {
		t.Errorf("expected 2 user_peers, got %d", properties["user_peers"])
	}

	if properties["store_engine"] != types.FileStoreEngine {
		t.Errorf("expected JsonFile, got %s", properties["store_engine"])
	}

	if properties["rules_with_src_posture_checks"] != 1 {
		t.Errorf("expected 1 rules_with_src_posture_checks, got %d", properties["rules_with_src_posture_checks"])
	}

	if properties["posture_checks"] != 2 {
		t.Errorf("expected 2 posture_checks, got %d", properties["posture_checks"])
	}

	if properties["rosenpass_enabled"] != 1 {
		t.Errorf("expected 1 rosenpass_enabled, got %d", properties["rosenpass_enabled"])
	}

	if properties["active_user_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_user_peers_last_day, got %d", properties["active_user_peers_last_day"])
	}

	if properties["active_users_last_day"] != 1 {
		t.Errorf("expected 1 active_users_last_day, got %d", properties["active_users_last_day"])
	}

	if properties["local_users_count"] != 1 {
		t.Errorf("expected 1 local_users_count, got %d", properties["local_users_count"])
	}
	if properties["idp_users_count"] != 1 {
		t.Errorf("expected 1 idp_users_count, got %d", properties["idp_users_count"])
	}
	if properties["embedded_idp_users_local"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_local, got %v", properties["embedded_idp_users_local"])
	}
	if properties["embedded_idp_users_zitadel"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_zitadel, got %v", properties["embedded_idp_users_zitadel"])
	}
	if properties["embedded_idp_count"] != 2 {
		t.Errorf("expected 2 embedded_idp_count, got %v", properties["embedded_idp_count"])
	}

	if properties["services"] != 2 {
		t.Errorf("expected 2 services, got %v", properties["services"])
	}
	if properties["services_enabled"] != 1 {
		t.Errorf("expected 1 services_enabled, got %v", properties["services_enabled"])
	}
	if properties["services_targets"] != 3 {
		t.Errorf("expected 3 services_targets, got %v", properties["services_targets"])
	}
	if properties["services_status_active"] != 1 {
		t.Errorf("expected 1 services_status_active, got %v", properties["services_status_active"])
	}
	if properties["services_status_pending"] != 1 {
		t.Errorf("expected 1 services_status_pending, got %v", properties["services_status_pending"])
	}
	if properties["services_status_error"] != 0 {
		t.Errorf("expected 0 services_status_error, got %v", properties["services_status_error"])
	}
	if properties["services_target_type_peer"] != 1 {
		t.Errorf("expected 1 services_target_type_peer, got %v", properties["services_target_type_peer"])
	}
	if properties["services_target_type_host"] != 1 {
		t.Errorf("expected 1 services_target_type_host, got %v", properties["services_target_type_host"])
	}
	if properties["services_target_type_domain"] != 1 {
		t.Errorf("expected 1 services_target_type_domain, got %v", properties["services_target_type_domain"])
	}
	if properties["services_auth_password"] != 1 {
		t.Errorf("expected 1 services_auth_password, got %v", properties["services_auth_password"])
	}
	if properties["services_auth_oidc"] != 1 {
		t.Errorf("expected 1 services_auth_oidc, got %v", properties["services_auth_oidc"])
	}
	if properties["services_auth_pin"] != 0 {
		t.Errorf("expected 0 services_auth_pin, got %v", properties["services_auth_pin"])
	}
	if properties["custom_domains"] != int64(3) {
		t.Errorf("expected 3 custom_domains, got %v", properties["custom_domains"])
	}
	if properties["custom_domains_validated"] != int64(2) {
		t.Errorf("expected 2 custom_domains_validated, got %v", properties["custom_domains_validated"])
	}
}

func TestExtractIdpType(t *testing.T) {
	tests := []struct {
		connectorID string
		expected    string
	}{
		{"okta-abc123def", "okta"},
		{"zitadel-d5uv82dra0haedlf6kv0", "zitadel"},
		{"entra-xyz789", "entra"},
		{"google-abc123", "google"},
		{"pocketid-abc123", "pocketid"},
		{"microsoft-abc123", "microsoft"},
		{"authentik-abc123", "authentik"},
		{"keycloak-d5uv82dra0haedlf6kv0", "keycloak"},
		{"local", "local"},
		{"d6jvvp69kmnc73c9pl40", "oidc"},
		{"", "oidc"},
	}

	for _, tt := range tests {
		t.Run(tt.connectorID, func(t *testing.T) {
			result := extractIdpType(tt.connectorID)
			if result != tt.expected {
				t.Errorf("extractIdpType(%q) = %q, want %q", tt.connectorID, result, tt.expected)
			}
		})
	}
}

package metrics

import (
	"context"
	"testing"

	nbdns "github.com/netbirdio/netbird/dns"
	"github.com/netbirdio/netbird/idp/dex"
	rpservice "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
	resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
	routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
	networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
	nbpeer "github.com/netbirdio/netbird/management/server/peer"
	"github.com/netbirdio/netbird/management/server/posture"
	"github.com/netbirdio/netbird/management/server/types"
	"github.com/netbirdio/netbird/route"
)

type mockDatasource struct{}

// GetAllConnectedPeers returns a map of connected peer IDs for use in tests with predefined information
func (mockDatasource) GetAllConnectedPeers() map[string]struct{} {
	return map[string]struct{}{
		"1": {},
	}
}

// GetAllAccounts returns a list of *server.Account for use in tests with predefined information
func (mockDatasource) GetAllAccounts(_ context.Context) []*types.Account {
	localUserID := dex.EncodeDexUserID("10", "local")
	idpUserID := dex.EncodeDexUserID("20", "zitadel-d5uv82dra0haedlf6kv0")
	oidcUserID := dex.EncodeDexUserID("30", "d6jvvp69kmnc73c9pl40")
	return []*types.Account{
		{
			Id:       "1",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: false,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1", Flags: nbpeer.Flags{ServerSSHAllowed: true, RosenpassEnabled: true}},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
					SourcePostureChecks: []string{"1"},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			PostureChecks: []*posture.Checks{
				{
					ID:   "1",
					Name: "test",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.1",
						},
					},
				},
				{
					ID:   "2",
					Name: "tes2",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.2",
						},
					},
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				localUserID: {
					Id:            localUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Services: []*rpservice.Service{
				{
					ID:      "svc1",
					Enabled: true,
					Targets: []*rpservice.Target{
						{TargetType: "peer"},
						{TargetType: "host"},
					},
					Auth: rpservice.AuthConfig{
						PasswordAuth: &rpservice.PasswordAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusActive)},
				},
				{
					ID:      "svc2",
					Enabled: false,
					Targets: []*rpservice.Target{
						{TargetType: "domain"},
					},
					Auth: rpservice.AuthConfig{
						BearerAuth: &rpservice.BearerAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusPending)},
				},
			},
		},
		{
			Id:       "2",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: true,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1"},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				idpUserID: {
					Id:            idpUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				oidcUserID: {
					Id:            oidcUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Networks: []*networkTypes.Network{
				{
					ID:        "1",
					AccountID: "1",
				},
			},
			NetworkResources: []*resourceTypes.NetworkResource{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
				{
					ID:        "2",
					AccountID: "1",
					NetworkID: "1",
				},
			},
			NetworkRouters: []*routerTypes.NetworkRouter{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
			},
		},
	}
}

// GetStoreEngine returns FileStoreEngine
func (mockDatasource) GetStoreEngine() types.Engine {
	return types.FileStoreEngine
}

// GetCustomDomainsCounts returns test custom domain counts.
func (mockDatasource) GetCustomDomainsCounts(_ context.Context) (int64, int64, error) {
	return 3, 2, nil
}

// TestGenerateProperties tests and validate the properties generation by using the mockDatasource for the Worker.generateProperties
func TestGenerateProperties(t *testing.T) {
	ds := mockDatasource{}
	worker := Worker{
		dataSource:  ds,
		connManager: ds,
		idpManager:  EmbeddedType,
	}

	properties := worker.generateProperties(context.Background())

	if properties["accounts"] != 2 {
		t.Errorf("expected 2 accounts, got %d", properties["accounts"])
	}
	if properties["peers"] != 2 {
		t.Errorf("expected 2 peers, got %d", properties["peers"])
	}
	if properties["routes"] != 2 {
		t.Errorf("expected 2 routes, got %d", properties["routes"])
	}
	if properties["networks"] != 1 {
		t.Errorf("expected 1 networks, got %d", properties["networks"])
	}
	if properties["network_resources"] != 2 {
		t.Errorf("expected 2 network_resources, got %d", properties["network_resources"])
	}
	if properties["network_routers"] != 1 {
		t.Errorf("expected 1 network_routers, got %d", properties["network_routers"])
	}
	if properties["rules"] != 4 {
		t.Errorf("expected 4 rules, got %d", properties["rules"])
	}
	if properties["users"] != 3 {
		t.Errorf("expected 3 users, got %d", properties["users"])
	}
	if properties["setup_keys_usage"] != 2 {
		t.Errorf("expected 1 setup_keys_usage, got %d", properties["setup_keys_usage"])
	}
	if properties["pats"] != 5 {
		t.Errorf("expected 5 personal_access_tokens, got %d", properties["pats"])
	}
	if properties["peers_ssh_enabled"] != 2 {
		t.Errorf("expected 2 peers_ssh_enabled, got %d", properties["peers_ssh_enabled"])
	}
	if properties["routes_with_routing_groups"] != 2 {
		t.Errorf("expected 2 routes_with_routing_groups, got %d", properties["routes_with_routing_groups"])
	}
	if properties["rules_protocol_tcp"] != 4 {
		t.Errorf("expected 4 rules_protocol_tcp, got %d", properties["rules_protocol_tcp"])
	}
	if properties["rules_direction_oneway"] != 2 {
		t.Errorf("expected 2 rules_direction_oneway, got %d", properties["rules_direction_oneway"])
	}

	if properties["active_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_peers_last_day, got %d", properties["active_peers_last_day"])
	}
	if properties["min_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 min_active_peer_version, got %s", properties["min_active_peer_version"])
	}
	if properties["max_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 max_active_peer_version, got %s", properties["max_active_peer_version"])
	}

	if properties["peers_login_expiration_enabled"] != 2 {
		t.Errorf("expected 2 peers_login_expiration_enabled, got %d", properties["peers_login_expiration_enabled"])
	}

	if properties["service_users"] != 2 {
		t.Errorf("expected 2 service_users, got %d", properties["service_users"])
	}

	if properties["peer_os_linux"] != 2 {
		t.Errorf("expected 2 peer_os_linux, got %d", properties["peer_os_linux"])
	}

	if properties["ephemeral_peers_setup_keys"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["ephemeral_peers_setup_keys_usage"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys_usage, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["nameservers"] != 2 {
		t.Errorf("expected 2 nameservers, got %d", properties["nameservers"])
	}

	if properties["groups"] != 4 {
		t.Errorf("expected 4 groups, got %d", properties["groups"])
	}

	if properties["user_peers"] != 2 {
		t.Errorf("expected 2 user_peers, got %d", properties["user_peers"])
	}

	if properties["store_engine"] != types.FileStoreEngine {
		t.Errorf("expected JsonFile, got %s", properties["store_engine"])
	}

	if properties["rules_with_src_posture_checks"] != 1 {
		t.Errorf("expected 1 rules_with_src_posture_checks, got %d", properties["rules_with_src_posture_checks"])
	}

	if properties["posture_checks"] != 2 {
		t.Errorf("expected 2 posture_checks, got %d", properties["posture_checks"])
	}

	if properties["rosenpass_enabled"] != 1 {
		t.Errorf("expected 1 rosenpass_enabled, got %d", properties["rosenpass_enabled"])
	}

	if properties["active_user_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_user_peers_last_day, got %d", properties["active_user_peers_last_day"])
	}

	if properties["active_users_last_day"] != 1 {
		t.Errorf("expected 1 active_users_last_day, got %d", properties["active_users_last_day"])
	}

	if properties["local_users_count"] != 1 {
		t.Errorf("expected 1 local_users_count, got %d", properties["local_users_count"])
	}
	if properties["idp_users_count"] != 1 {
		t.Errorf("expected 1 idp_users_count, got %d", properties["idp_users_count"])
	}
	if properties["embedded_idp_users_local"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_local, got %v", properties["embedded_idp_users_local"])
	}
	if properties["embedded_idp_users_zitadel"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_zitadel, got %v", properties["embedded_idp_users_zitadel"])
	}
	if properties["embedded_idp_count"] != 2 {
		t.Errorf("expected 2 embedded_idp_count, got %v", properties["embedded_idp_count"])
	}

	if properties["services"] != 2 {
		t.Errorf("expected 2 services, got %v", properties["services"])
	}
	if properties["services_enabled"] != 1 {
		t.Errorf("expected 1 services_enabled, got %v", properties["services_enabled"])
	}
	if properties["services_targets"] != 3 {
		t.Errorf("expected 3 services_targets, got %v", properties["services_targets"])
	}
	if properties["services_status_active"] != 1 {
		t.Errorf("expected 1 services_status_active, got %v", properties["services_status_active"])
	}
	if properties["services_status_pending"] != 1 {
		t.Errorf("expected 1 services_status_pending, got %v", properties["services_status_pending"])
	}
	if properties["services_status_error"] != 0 {
		t.Errorf("expected 0 services_status_error, got %v", properties["services_status_error"])
	}
	if properties["services_target_type_peer"] != 1 {
		t.Errorf("expected 1 services_target_type_peer, got %v", properties["services_target_type_peer"])
	}
	if properties["services_target_type_host"] != 1 {
		t.Errorf("expected 1 services_target_type_host, got %v", properties["services_target_type_host"])
	}
	if properties["services_target_type_domain"] != 1 {
		t.Errorf("expected 1 services_target_type_domain, got %v", properties["services_target_type_domain"])
	}
	if properties["services_auth_password"] != 1 {
		t.Errorf("expected 1 services_auth_password, got %v", properties["services_auth_password"])
	}
	if properties["services_auth_oidc"] != 1 {
		t.Errorf("expected 1 services_auth_oidc, got %v", properties["services_auth_oidc"])
	}
	if properties["services_auth_pin"] != 0 {
		t.Errorf("expected 0 services_auth_pin, got %v", properties["services_auth_pin"])
	}
	if properties["custom_domains"] != int64(3) {
		t.Errorf("expected 3 custom_domains, got %v", properties["custom_domains"])
	}
	if properties["custom_domains_validated"] != int64(2) {
		t.Errorf("expected 2 custom_domains_validated, got %v", properties["custom_domains_validated"])
	}
}

func TestExtractIdpType(t *testing.T) {
	tests := []struct {
		connectorID string
		expected    string
	}{
		{"okta-abc123def", "okta"},
		{"zitadel-d5uv82dra0haedlf6kv0", "zitadel"},
		{"entra-xyz789", "entra"},
		{"google-abc123", "google"},
		{"pocketid-abc123", "pocketid"},
		{"microsoft-abc123", "microsoft"},
		{"authentik-abc123", "authentik"},
		{"keycloak-d5uv82dra0haedlf6kv0", "keycloak"},
		{"local", "local"},
		{"d6jvvp69kmnc73c9pl40", "oidc"},
		{"", "oidc"},
	}

	for _, tt := range tests {
		t.Run(tt.connectorID, func(t *testing.T) {
			result := extractIdpType(tt.connectorID)
			if result != tt.expected {
				t.Errorf("extractIdpType(%q) = %q, want %q", tt.connectorID, result, tt.expected)
			}
		})
	}
}

-    t.Errorf("expected 4 personal_access_tokens, got %d", properties["pats"])

+    t.Errorf("expected 5 personal_access_tokens, got %d", properties["pats"])


Edit

	if properties["idp_users_count"] != 1 {
		t.Errorf("expected 1 idp_users_count, got %d", properties["idp_users_count"])
	}

	if properties["idp_users_count"] != 2 {
		t.Errorf("expected 2 idp_users_count, got %d", properties["idp_users_count"])
	}


The file /Users/misha/Documents/GolandProjects/netbird/netbird/management/server/metrics/selfhosted_test.go has been updated successfully.

package metrics

import (
	"context"
	"testing"

	nbdns "github.com/netbirdio/netbird/dns"
	"github.com/netbirdio/netbird/idp/dex"
	rpservice "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
	resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
	routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
	networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
	nbpeer "github.com/netbirdio/netbird/management/server/peer"
	"github.com/netbirdio/netbird/management/server/posture"
	"github.com/netbirdio/netbird/management/server/types"
	"github.com/netbirdio/netbird/route"
)

type mockDatasource struct{}

// GetAllConnectedPeers returns a map of connected peer IDs for use in tests with predefined information
func (mockDatasource) GetAllConnectedPeers() map[string]struct{} {
	return map[string]struct{}{
		"1": {},
	}
}

// GetAllAccounts returns a list of *server.Account for use in tests with predefined information
func (mockDatasource) GetAllAccounts(_ context.Context) []*types.Account {
	localUserID := dex.EncodeDexUserID("10", "local")
	idpUserID := dex.EncodeDexUserID("20", "zitadel-d5uv82dra0haedlf6kv0")
	oidcUserID := dex.EncodeDexUserID("30", "d6jvvp69kmnc73c9pl40")
	return []*types.Account{
		{
			Id:       "1",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: false,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1", Flags: nbpeer.Flags{ServerSSHAllowed: true, RosenpassEnabled: true}},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
					SourcePostureChecks: []string{"1"},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			PostureChecks: []*posture.Checks{
				{
					ID:   "1",
					Name: "test",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.1",
						},
					},
				},
				{
					ID:   "2",
					Name: "tes2",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.2",
						},
					},
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				localUserID: {
					Id:            localUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Services: []*rpservice.Service{
				{
					ID:      "svc1",
					Enabled: true,
					Targets: []*rpservice.Target{
						{TargetType: "peer"},
						{TargetType: "host"},
					},
					Auth: rpservice.AuthConfig{
						PasswordAuth: &rpservice.PasswordAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusActive)},
				},
				{
					ID:      "svc2",
					Enabled: false,
					Targets: []*rpservice.Target{
						{TargetType: "domain"},
					},
					Auth: rpservice.AuthConfig{
						BearerAuth: &rpservice.BearerAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusPending)},
				},
			},
		},
		{
			Id:       "2",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: true,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1"},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				idpUserID: {
					Id:            idpUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				oidcUserID: {
					Id:            oidcUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Networks: []*networkTypes.Network{
				{
					ID:        "1",
					AccountID: "1",
				},
			},
			NetworkResources: []*resourceTypes.NetworkResource{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
				{
					ID:        "2",
					AccountID: "1",
					NetworkID: "1",
				},
			},
			NetworkRouters: []*routerTypes.NetworkRouter{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
			},
		},
	}
}

// GetStoreEngine returns FileStoreEngine
func (mockDatasource) GetStoreEngine() types.Engine {
	return types.FileStoreEngine
}

// GetCustomDomainsCounts returns test custom domain counts.
func (mockDatasource) GetCustomDomainsCounts(_ context.Context) (int64, int64, error) {
	return 3, 2, nil
}

// TestGenerateProperties tests and validate the properties generation by using the mockDatasource for the Worker.generateProperties
func TestGenerateProperties(t *testing.T) {
	ds := mockDatasource{}
	worker := Worker{
		dataSource:  ds,
		connManager: ds,
		idpManager:  EmbeddedType,
	}

	properties := worker.generateProperties(context.Background())

	if properties["accounts"] != 2 {
		t.Errorf("expected 2 accounts, got %d", properties["accounts"])
	}
	if properties["peers"] != 2 {
		t.Errorf("expected 2 peers, got %d", properties["peers"])
	}
	if properties["routes"] != 2 {
		t.Errorf("expected 2 routes, got %d", properties["routes"])
	}
	if properties["networks"] != 1 {
		t.Errorf("expected 1 networks, got %d", properties["networks"])
	}
	if properties["network_resources"] != 2 {
		t.Errorf("expected 2 network_resources, got %d", properties["network_resources"])
	}
	if properties["network_routers"] != 1 {
		t.Errorf("expected 1 network_routers, got %d", properties["network_routers"])
	}
	if properties["rules"] != 4 {
		t.Errorf("expected 4 rules, got %d", properties["rules"])
	}
	if properties["users"] != 3 {
		t.Errorf("expected 3 users, got %d", properties["users"])
	}
	if properties["setup_keys_usage"] != 2 {
		t.Errorf("expected 1 setup_keys_usage, got %d", properties["setup_keys_usage"])
	}
	if properties["pats"] != 5 {
		t.Errorf("expected 5 personal_access_tokens, got %d", properties["pats"])
	}
	if properties["peers_ssh_enabled"] != 2 {
		t.Errorf("expected 2 peers_ssh_enabled, got %d", properties["peers_ssh_enabled"])
	}
	if properties["routes_with_routing_groups"] != 2 {
		t.Errorf("expected 2 routes_with_routing_groups, got %d", properties["routes_with_routing_groups"])
	}
	if properties["rules_protocol_tcp"] != 4 {
		t.Errorf("expected 4 rules_protocol_tcp, got %d", properties["rules_protocol_tcp"])
	}
	if properties["rules_direction_oneway"] != 2 {
		t.Errorf("expected 2 rules_direction_oneway, got %d", properties["rules_direction_oneway"])
	}

	if properties["active_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_peers_last_day, got %d", properties["active_peers_last_day"])
	}
	if properties["min_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 min_active_peer_version, got %s", properties["min_active_peer_version"])
	}
	if properties["max_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 max_active_peer_version, got %s", properties["max_active_peer_version"])
	}

	if properties["peers_login_expiration_enabled"] != 2 {
		t.Errorf("expected 2 peers_login_expiration_enabled, got %d", properties["peers_login_expiration_enabled"])
	}

	if properties["service_users"] != 2 {
		t.Errorf("expected 2 service_users, got %d", properties["service_users"])
	}

	if properties["peer_os_linux"] != 2 {
		t.Errorf("expected 2 peer_os_linux, got %d", properties["peer_os_linux"])
	}

	if properties["ephemeral_peers_setup_keys"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["ephemeral_peers_setup_keys_usage"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys_usage, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["nameservers"] != 2 {
		t.Errorf("expected 2 nameservers, got %d", properties["nameservers"])
	}

	if properties["groups"] != 4 {
		t.Errorf("expected 4 groups, got %d", properties["groups"])
	}

	if properties["user_peers"] != 2 {
		t.Errorf("expected 2 user_peers, got %d", properties["user_peers"])
	}

	if properties["store_engine"] != types.FileStoreEngine {
		t.Errorf("expected JsonFile, got %s", properties["store_engine"])
	}

	if properties["rules_with_src_posture_checks"] != 1 {
		t.Errorf("expected 1 rules_with_src_posture_checks, got %d", properties["rules_with_src_posture_checks"])
	}

	if properties["posture_checks"] != 2 {
		t.Errorf("expected 2 posture_checks, got %d", properties["posture_checks"])
	}

	if properties["rosenpass_enabled"] != 1 {
		t.Errorf("expected 1 rosenpass_enabled, got %d", properties["rosenpass_enabled"])
	}

	if properties["active_user_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_user_peers_last_day, got %d", properties["active_user_peers_last_day"])
	}

	if properties["active_users_last_day"] != 1 {
		t.Errorf("expected 1 active_users_last_day, got %d", properties["active_users_last_day"])
	}

	if properties["local_users_count"] != 1 {
		t.Errorf("expected 1 local_users_count, got %d", properties["local_users_count"])
	}
	if properties["idp_users_count"] != 1 {
		t.Errorf("expected 1 idp_users_count, got %d", properties["idp_users_count"])
	}
	if properties["embedded_idp_users_local"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_local, got %v", properties["embedded_idp_users_local"])
	}
	if properties["embedded_idp_users_zitadel"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_zitadel, got %v", properties["embedded_idp_users_zitadel"])
	}
	if properties["embedded_idp_count"] != 2 {
		t.Errorf("expected 2 embedded_idp_count, got %v", properties["embedded_idp_count"])
	}

	if properties["services"] != 2 {
		t.Errorf("expected 2 services, got %v", properties["services"])
	}
	if properties["services_enabled"] != 1 {
		t.Errorf("expected 1 services_enabled, got %v", properties["services_enabled"])
	}
	if properties["services_targets"] != 3 {
		t.Errorf("expected 3 services_targets, got %v", properties["services_targets"])
	}
	if properties["services_status_active"] != 1 {
		t.Errorf("expected 1 services_status_active, got %v", properties["services_status_active"])
	}
	if properties["services_status_pending"] != 1 {
		t.Errorf("expected 1 services_status_pending, got %v", properties["services_status_pending"])
	}
	if properties["services_status_error"] != 0 {
		t.Errorf("expected 0 services_status_error, got %v", properties["services_status_error"])
	}
	if properties["services_target_type_peer"] != 1 {
		t.Errorf("expected 1 services_target_type_peer, got %v", properties["services_target_type_peer"])
	}
	if properties["services_target_type_host"] != 1 {
		t.Errorf("expected 1 services_target_type_host, got %v", properties["services_target_type_host"])
	}
	if properties["services_target_type_domain"] != 1 {
		t.Errorf("expected 1 services_target_type_domain, got %v", properties["services_target_type_domain"])
	}
	if properties["services_auth_password"] != 1 {
		t.Errorf("expected 1 services_auth_password, got %v", properties["services_auth_password"])
	}
	if properties["services_auth_oidc"] != 1 {
		t.Errorf("expected 1 services_auth_oidc, got %v", properties["services_auth_oidc"])
	}
	if properties["services_auth_pin"] != 0 {
		t.Errorf("expected 0 services_auth_pin, got %v", properties["services_auth_pin"])
	}
	if properties["custom_domains"] != int64(3) {
		t.Errorf("expected 3 custom_domains, got %v", properties["custom_domains"])
	}
	if properties["custom_domains_validated"] != int64(2) {
		t.Errorf("expected 2 custom_domains_validated, got %v", properties["custom_domains_validated"])
	}
}

func TestExtractIdpType(t *testing.T) {
	tests := []struct {
		connectorID string
		expected    string
	}{
		{"okta-abc123def", "okta"},
		{"zitadel-d5uv82dra0haedlf6kv0", "zitadel"},
		{"entra-xyz789", "entra"},
		{"google-abc123", "google"},
		{"pocketid-abc123", "pocketid"},
		{"microsoft-abc123", "microsoft"},
		{"authentik-abc123", "authentik"},
		{"keycloak-d5uv82dra0haedlf6kv0", "keycloak"},
		{"local", "local"},
		{"d6jvvp69kmnc73c9pl40", "oidc"},
		{"", "oidc"},
	}

	for _, tt := range tests {
		t.Run(tt.connectorID, func(t *testing.T) {
			result := extractIdpType(tt.connectorID)
			if result != tt.expected {
				t.Errorf("extractIdpType(%q) = %q, want %q", tt.connectorID, result, tt.expected)
			}
		})
	}
}

package metrics

import (
	"context"
	"testing"

	nbdns "github.com/netbirdio/netbird/dns"
	"github.com/netbirdio/netbird/idp/dex"
	rpservice "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
	resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
	routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
	networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
	nbpeer "github.com/netbirdio/netbird/management/server/peer"
	"github.com/netbirdio/netbird/management/server/posture"
	"github.com/netbirdio/netbird/management/server/types"
	"github.com/netbirdio/netbird/route"
)

type mockDatasource struct{}

// GetAllConnectedPeers returns a map of connected peer IDs for use in tests with predefined information
func (mockDatasource) GetAllConnectedPeers() map[string]struct{} {
	return map[string]struct{}{
		"1": {},
	}
}

// GetAllAccounts returns a list of *server.Account for use in tests with predefined information
func (mockDatasource) GetAllAccounts(_ context.Context) []*types.Account {
	localUserID := dex.EncodeDexUserID("10", "local")
	idpUserID := dex.EncodeDexUserID("20", "zitadel-d5uv82dra0haedlf6kv0")
	oidcUserID := dex.EncodeDexUserID("30", "d6jvvp69kmnc73c9pl40")
	return []*types.Account{
		{
			Id:       "1",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: false,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1", Flags: nbpeer.Flags{ServerSSHAllowed: true, RosenpassEnabled: true}},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
					SourcePostureChecks: []string{"1"},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			PostureChecks: []*posture.Checks{
				{
					ID:   "1",
					Name: "test",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.1",
						},
					},
				},
				{
					ID:   "2",
					Name: "tes2",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.2",
						},
					},
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				localUserID: {
					Id:            localUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Services: []*rpservice.Service{
				{
					ID:      "svc1",
					Enabled: true,
					Targets: []*rpservice.Target{
						{TargetType: "peer"},
						{TargetType: "host"},
					},
					Auth: rpservice.AuthConfig{
						PasswordAuth: &rpservice.PasswordAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusActive)},
				},
				{
					ID:      "svc2",
					Enabled: false,
					Targets: []*rpservice.Target{
						{TargetType: "domain"},
					},
					Auth: rpservice.AuthConfig{
						BearerAuth: &rpservice.BearerAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusPending)},
				},
			},
		},
		{
			Id:       "2",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: true,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1"},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				idpUserID: {
					Id:            idpUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				oidcUserID: {
					Id:            oidcUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Networks: []*networkTypes.Network{
				{
					ID:        "1",
					AccountID: "1",
				},
			},
			NetworkResources: []*resourceTypes.NetworkResource{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
				{
					ID:        "2",
					AccountID: "1",
					NetworkID: "1",
				},
			},
			NetworkRouters: []*routerTypes.NetworkRouter{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
			},
		},
	}
}

// GetStoreEngine returns FileStoreEngine
func (mockDatasource) GetStoreEngine() types.Engine {
	return types.FileStoreEngine
}

// GetCustomDomainsCounts returns test custom domain counts.
func (mockDatasource) GetCustomDomainsCounts(_ context.Context) (int64, int64, error) {
	return 3, 2, nil
}

// TestGenerateProperties tests and validate the properties generation by using the mockDatasource for the Worker.generateProperties
func TestGenerateProperties(t *testing.T) {
	ds := mockDatasource{}
	worker := Worker{
		dataSource:  ds,
		connManager: ds,
		idpManager:  EmbeddedType,
	}

	properties := worker.generateProperties(context.Background())

	if properties["accounts"] != 2 {
		t.Errorf("expected 2 accounts, got %d", properties["accounts"])
	}
	if properties["peers"] != 2 {
		t.Errorf("expected 2 peers, got %d", properties["peers"])
	}
	if properties["routes"] != 2 {
		t.Errorf("expected 2 routes, got %d", properties["routes"])
	}
	if properties["networks"] != 1 {
		t.Errorf("expected 1 networks, got %d", properties["networks"])
	}
	if properties["network_resources"] != 2 {
		t.Errorf("expected 2 network_resources, got %d", properties["network_resources"])
	}
	if properties["network_routers"] != 1 {
		t.Errorf("expected 1 network_routers, got %d", properties["network_routers"])
	}
	if properties["rules"] != 4 {
		t.Errorf("expected 4 rules, got %d", properties["rules"])
	}
	if properties["users"] != 3 {
		t.Errorf("expected 3 users, got %d", properties["users"])
	}
	if properties["setup_keys_usage"] != 2 {
		t.Errorf("expected 1 setup_keys_usage, got %d", properties["setup_keys_usage"])
	}
	if properties["pats"] != 5 {
		t.Errorf("expected 5 personal_access_tokens, got %d", properties["pats"])
	}
	if properties["peers_ssh_enabled"] != 2 {
		t.Errorf("expected 2 peers_ssh_enabled, got %d", properties["peers_ssh_enabled"])
	}
	if properties["routes_with_routing_groups"] != 2 {
		t.Errorf("expected 2 routes_with_routing_groups, got %d", properties["routes_with_routing_groups"])
	}
	if properties["rules_protocol_tcp"] != 4 {
		t.Errorf("expected 4 rules_protocol_tcp, got %d", properties["rules_protocol_tcp"])
	}
	if properties["rules_direction_oneway"] != 2 {
		t.Errorf("expected 2 rules_direction_oneway, got %d", properties["rules_direction_oneway"])
	}

	if properties["active_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_peers_last_day, got %d", properties["active_peers_last_day"])
	}
	if properties["min_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 min_active_peer_version, got %s", properties["min_active_peer_version"])
	}
	if properties["max_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 max_active_peer_version, got %s", properties["max_active_peer_version"])
	}

	if properties["peers_login_expiration_enabled"] != 2 {
		t.Errorf("expected 2 peers_login_expiration_enabled, got %d", properties["peers_login_expiration_enabled"])
	}

	if properties["service_users"] != 2 {
		t.Errorf("expected 2 service_users, got %d", properties["service_users"])
	}

	if properties["peer_os_linux"] != 2 {
		t.Errorf("expected 2 peer_os_linux, got %d", properties["peer_os_linux"])
	}

	if properties["ephemeral_peers_setup_keys"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["ephemeral_peers_setup_keys_usage"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys_usage, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["nameservers"] != 2 {
		t.Errorf("expected 2 nameservers, got %d", properties["nameservers"])
	}

	if properties["groups"] != 4 {
		t.Errorf("expected 4 groups, got %d", properties["groups"])
	}

	if properties["user_peers"] != 2 {
		t.Errorf("expected 2 user_peers, got %d", properties["user_peers"])
	}

	if properties["store_engine"] != types.FileStoreEngine {
		t.Errorf("expected JsonFile, got %s", properties["store_engine"])
	}

	if properties["rules_with_src_posture_checks"] != 1 {
		t.Errorf("expected 1 rules_with_src_posture_checks, got %d", properties["rules_with_src_posture_checks"])
	}

	if properties["posture_checks"] != 2 {
		t.Errorf("expected 2 posture_checks, got %d", properties["posture_checks"])
	}

	if properties["rosenpass_enabled"] != 1 {
		t.Errorf("expected 1 rosenpass_enabled, got %d", properties["rosenpass_enabled"])
	}

	if properties["active_user_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_user_peers_last_day, got %d", properties["active_user_peers_last_day"])
	}

	if properties["active_users_last_day"] != 1 {
		t.Errorf("expected 1 active_users_last_day, got %d", properties["active_users_last_day"])
	}

	if properties["local_users_count"] != 1 {
		t.Errorf("expected 1 local_users_count, got %d", properties["local_users_count"])
	}
	if properties["idp_users_count"] != 2 {
		t.Errorf("expected 2 idp_users_count, got %d", properties["idp_users_count"])
	}
	if properties["embedded_idp_users_local"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_local, got %v", properties["embedded_idp_users_local"])
	}
	if properties["embedded_idp_users_zitadel"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_zitadel, got %v", properties["embedded_idp_users_zitadel"])
	}
	if properties["embedded_idp_count"] != 2 {
		t.Errorf("expected 2 embedded_idp_count, got %v", properties["embedded_idp_count"])
	}

	if properties["services"] != 2 {
		t.Errorf("expected 2 services, got %v", properties["services"])
	}
	if properties["services_enabled"] != 1 {
		t.Errorf("expected 1 services_enabled, got %v", properties["services_enabled"])
	}
	if properties["services_targets"] != 3 {
		t.Errorf("expected 3 services_targets, got %v", properties["services_targets"])
	}
	if properties["services_status_active"] != 1 {
		t.Errorf("expected 1 services_status_active, got %v", properties["services_status_active"])
	}
	if properties["services_status_pending"] != 1 {
		t.Errorf("expected 1 services_status_pending, got %v", properties["services_status_pending"])
	}
	if properties["services_status_error"] != 0 {
		t.Errorf("expected 0 services_status_error, got %v", properties["services_status_error"])
	}
	if properties["services_target_type_peer"] != 1 {
		t.Errorf("expected 1 services_target_type_peer, got %v", properties["services_target_type_peer"])
	}
	if properties["services_target_type_host"] != 1 {
		t.Errorf("expected 1 services_target_type_host, got %v", properties["services_target_type_host"])
	}
	if properties["services_target_type_domain"] != 1 {
		t.Errorf("expected 1 services_target_type_domain, got %v", properties["services_target_type_domain"])
	}
	if properties["services_auth_password"] != 1 {
		t.Errorf("expected 1 services_auth_password, got %v", properties["services_auth_password"])
	}
	if properties["services_auth_oidc"] != 1 {
		t.Errorf("expected 1 services_auth_oidc, got %v", properties["services_auth_oidc"])
	}
	if properties["services_auth_pin"] != 0 {
		t.Errorf("expected 0 services_auth_pin, got %v", properties["services_auth_pin"])
	}
	if properties["custom_domains"] != int64(3) {
		t.Errorf("expected 3 custom_domains, got %v", properties["custom_domains"])
	}
	if properties["custom_domains_validated"] != int64(2) {
		t.Errorf("expected 2 custom_domains_validated, got %v", properties["custom_domains_validated"])
	}
}

func TestExtractIdpType(t *testing.T) {
	tests := []struct {
		connectorID string
		expected    string
	}{
		{"okta-abc123def", "okta"},
		{"zitadel-d5uv82dra0haedlf6kv0", "zitadel"},
		{"entra-xyz789", "entra"},
		{"google-abc123", "google"},
		{"pocketid-abc123", "pocketid"},
		{"microsoft-abc123", "microsoft"},
		{"authentik-abc123", "authentik"},
		{"keycloak-d5uv82dra0haedlf6kv0", "keycloak"},
		{"local", "local"},
		{"d6jvvp69kmnc73c9pl40", "oidc"},
		{"", "oidc"},
	}

	for _, tt := range tests {
		t.Run(tt.connectorID, func(t *testing.T) {
			result := extractIdpType(tt.connectorID)
			if result != tt.expected {
				t.Errorf("extractIdpType(%q) = %q, want %q", tt.connectorID, result, tt.expected)
			}
		})
	}
}

-  if properties["idp_users_count"] != 1 {

-    t.Errorf("expected 1 idp_users_count, got %d", properties["idp_users_count"])

+  if properties["idp_users_count"] != 2 {

+    t.Errorf("expected 2 idp_users_count, got %d", properties["idp_users_count"])


	if properties["embedded_idp_users_zitadel"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_zitadel, got %v", properties["embedded_idp_users_zitadel"])
	}
	if properties["embedded_idp_count"] != 2 {
		t.Errorf("expected 2 embedded_idp_count, got %v", properties["embedded_idp_count"])
	}

	if properties["embedded_idp_users_zitadel"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_zitadel, got %v", properties["embedded_idp_users_zitadel"])
	}
	if properties["embedded_idp_users_oidc"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_oidc, got %v", properties["embedded_idp_users_oidc"])
	}
	if properties["embedded_idp_count"] != 3 {
		t.Errorf("expected 3 embedded_idp_count, got %v", properties["embedded_idp_count"])
	}


package metrics

import (
	"context"
	"testing"

	nbdns "github.com/netbirdio/netbird/dns"
	"github.com/netbirdio/netbird/idp/dex"
	rpservice "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
	resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
	routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
	networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
	nbpeer "github.com/netbirdio/netbird/management/server/peer"
	"github.com/netbirdio/netbird/management/server/posture"
	"github.com/netbirdio/netbird/management/server/types"
	"github.com/netbirdio/netbird/route"
)

type mockDatasource struct{}

// GetAllConnectedPeers returns a map of connected peer IDs for use in tests with predefined information
func (mockDatasource) GetAllConnectedPeers() map[string]struct{} {
	return map[string]struct{}{
		"1": {},
	}
}

// GetAllAccounts returns a list of *server.Account for use in tests with predefined information
func (mockDatasource) GetAllAccounts(_ context.Context) []*types.Account {
	localUserID := dex.EncodeDexUserID("10", "local")
	idpUserID := dex.EncodeDexUserID("20", "zitadel-d5uv82dra0haedlf6kv0")
	oidcUserID := dex.EncodeDexUserID("30", "d6jvvp69kmnc73c9pl40")
	return []*types.Account{
		{
			Id:       "1",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: false,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1", Flags: nbpeer.Flags{ServerSSHAllowed: true, RosenpassEnabled: true}},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
					SourcePostureChecks: []string{"1"},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			PostureChecks: []*posture.Checks{
				{
					ID:   "1",
					Name: "test",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.1",
						},
					},
				},
				{
					ID:   "2",
					Name: "tes2",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.2",
						},
					},
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				localUserID: {
					Id:            localUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Services: []*rpservice.Service{
				{
					ID:      "svc1",
					Enabled: true,
					Targets: []*rpservice.Target{
						{TargetType: "peer"},
						{TargetType: "host"},
					},
					Auth: rpservice.AuthConfig{
						PasswordAuth: &rpservice.PasswordAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusActive)},
				},
				{
					ID:      "svc2",
					Enabled: false,
					Targets: []*rpservice.Target{
						{TargetType: "domain"},
					},
					Auth: rpservice.AuthConfig{
						BearerAuth: &rpservice.BearerAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusPending)},
				},
			},
		},
		{
			Id:       "2",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: true,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1"},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				idpUserID: {
					Id:            idpUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				oidcUserID: {
					Id:            oidcUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Networks: []*networkTypes.Network{
				{
					ID:        "1",
					AccountID: "1",
				},
			},
			NetworkResources: []*resourceTypes.NetworkResource{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
				{
					ID:        "2",
					AccountID: "1",
					NetworkID: "1",
				},
			},
			NetworkRouters: []*routerTypes.NetworkRouter{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
			},
		},
	}
}

// GetStoreEngine returns FileStoreEngine
func (mockDatasource) GetStoreEngine() types.Engine {
	return types.FileStoreEngine
}

// GetCustomDomainsCounts returns test custom domain counts.
func (mockDatasource) GetCustomDomainsCounts(_ context.Context) (int64, int64, error) {
	return 3, 2, nil
}

// TestGenerateProperties tests and validate the properties generation by using the mockDatasource for the Worker.generateProperties
func TestGenerateProperties(t *testing.T) {
	ds := mockDatasource{}
	worker := Worker{
		dataSource:  ds,
		connManager: ds,
		idpManager:  EmbeddedType,
	}

	properties := worker.generateProperties(context.Background())

	if properties["accounts"] != 2 {
		t.Errorf("expected 2 accounts, got %d", properties["accounts"])
	}
	if properties["peers"] != 2 {
		t.Errorf("expected 2 peers, got %d", properties["peers"])
	}
	if properties["routes"] != 2 {
		t.Errorf("expected 2 routes, got %d", properties["routes"])
	}
	if properties["networks"] != 1 {
		t.Errorf("expected 1 networks, got %d", properties["networks"])
	}
	if properties["network_resources"] != 2 {
		t.Errorf("expected 2 network_resources, got %d", properties["network_resources"])
	}
	if properties["network_routers"] != 1 {
		t.Errorf("expected 1 network_routers, got %d", properties["network_routers"])
	}
	if properties["rules"] != 4 {
		t.Errorf("expected 4 rules, got %d", properties["rules"])
	}
	if properties["users"] != 3 {
		t.Errorf("expected 3 users, got %d", properties["users"])
	}
	if properties["setup_keys_usage"] != 2 {
		t.Errorf("expected 1 setup_keys_usage, got %d", properties["setup_keys_usage"])
	}
	if properties["pats"] != 5 {
		t.Errorf("expected 5 personal_access_tokens, got %d", properties["pats"])
	}
	if properties["peers_ssh_enabled"] != 2 {
		t.Errorf("expected 2 peers_ssh_enabled, got %d", properties["peers_ssh_enabled"])
	}
	if properties["routes_with_routing_groups"] != 2 {
		t.Errorf("expected 2 routes_with_routing_groups, got %d", properties["routes_with_routing_groups"])
	}
	if properties["rules_protocol_tcp"] != 4 {
		t.Errorf("expected 4 rules_protocol_tcp, got %d", properties["rules_protocol_tcp"])
	}
	if properties["rules_direction_oneway"] != 2 {
		t.Errorf("expected 2 rules_direction_oneway, got %d", properties["rules_direction_oneway"])
	}

	if properties["active_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_peers_last_day, got %d", properties["active_peers_last_day"])
	}
	if properties["min_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 min_active_peer_version, got %s", properties["min_active_peer_version"])
	}
	if properties["max_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 max_active_peer_version, got %s", properties["max_active_peer_version"])
	}

	if properties["peers_login_expiration_enabled"] != 2 {
		t.Errorf("expected 2 peers_login_expiration_enabled, got %d", properties["peers_login_expiration_enabled"])
	}

	if properties["service_users"] != 2 {
		t.Errorf("expected 2 service_users, got %d", properties["service_users"])
	}

	if properties["peer_os_linux"] != 2 {
		t.Errorf("expected 2 peer_os_linux, got %d", properties["peer_os_linux"])
	}

	if properties["ephemeral_peers_setup_keys"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["ephemeral_peers_setup_keys_usage"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys_usage, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["nameservers"] != 2 {
		t.Errorf("expected 2 nameservers, got %d", properties["nameservers"])
	}

	if properties["groups"] != 4 {
		t.Errorf("expected 4 groups, got %d", properties["groups"])
	}

	if properties["user_peers"] != 2 {
		t.Errorf("expected 2 user_peers, got %d", properties["user_peers"])
	}

	if properties["store_engine"] != types.FileStoreEngine {
		t.Errorf("expected JsonFile, got %s", properties["store_engine"])
	}

	if properties["rules_with_src_posture_checks"] != 1 {
		t.Errorf("expected 1 rules_with_src_posture_checks, got %d", properties["rules_with_src_posture_checks"])
	}

	if properties["posture_checks"] != 2 {
		t.Errorf("expected 2 posture_checks, got %d", properties["posture_checks"])
	}

	if properties["rosenpass_enabled"] != 1 {
		t.Errorf("expected 1 rosenpass_enabled, got %d", properties["rosenpass_enabled"])
	}

	if properties["active_user_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_user_peers_last_day, got %d", properties["active_user_peers_last_day"])
	}

	if properties["active_users_last_day"] != 1 {
		t.Errorf("expected 1 active_users_last_day, got %d", properties["active_users_last_day"])
	}

	if properties["local_users_count"] != 1 {
		t.Errorf("expected 1 local_users_count, got %d", properties["local_users_count"])
	}
	if properties["idp_users_count"] != 2 {
		t.Errorf("expected 2 idp_users_count, got %d", properties["idp_users_count"])
	}
	if properties["embedded_idp_users_local"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_local, got %v", properties["embedded_idp_users_local"])
	}
	if properties["embedded_idp_users_zitadel"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_zitadel, got %v", properties["embedded_idp_users_zitadel"])
	}
	if properties["embedded_idp_count"] != 2 {
		t.Errorf("expected 2 embedded_idp_count, got %v", properties["embedded_idp_count"])
	}

	if properties["services"] != 2 {
		t.Errorf("expected 2 services, got %v", properties["services"])
	}
	if properties["services_enabled"] != 1 {
		t.Errorf("expected 1 services_enabled, got %v", properties["services_enabled"])
	}
	if properties["services_targets"] != 3 {
		t.Errorf("expected 3 services_targets, got %v", properties["services_targets"])
	}
	if properties["services_status_active"] != 1 {
		t.Errorf("expected 1 services_status_active, got %v", properties["services_status_active"])
	}
	if properties["services_status_pending"] != 1 {
		t.Errorf("expected 1 services_status_pending, got %v", properties["services_status_pending"])
	}
	if properties["services_status_error"] != 0 {
		t.Errorf("expected 0 services_status_error, got %v", properties["services_status_error"])
	}
	if properties["services_target_type_peer"] != 1 {
		t.Errorf("expected 1 services_target_type_peer, got %v", properties["services_target_type_peer"])
	}
	if properties["services_target_type_host"] != 1 {
		t.Errorf("expected 1 services_target_type_host, got %v", properties["services_target_type_host"])
	}
	if properties["services_target_type_domain"] != 1 {
		t.Errorf("expected 1 services_target_type_domain, got %v", properties["services_target_type_domain"])
	}
	if properties["services_auth_password"] != 1 {
		t.Errorf("expected 1 services_auth_password, got %v", properties["services_auth_password"])
	}
	if properties["services_auth_oidc"] != 1 {
		t.Errorf("expected 1 services_auth_oidc, got %v", properties["services_auth_oidc"])
	}
	if properties["services_auth_pin"] != 0 {
		t.Errorf("expected 0 services_auth_pin, got %v", properties["services_auth_pin"])
	}
	if properties["custom_domains"] != int64(3) {
		t.Errorf("expected 3 custom_domains, got %v", properties["custom_domains"])
	}
	if properties["custom_domains_validated"] != int64(2) {
		t.Errorf("expected 2 custom_domains_validated, got %v", properties["custom_domains_validated"])
	}
}

func TestExtractIdpType(t *testing.T) {
	tests := []struct {
		connectorID string
		expected    string
	}{
		{"okta-abc123def", "okta"},
		{"zitadel-d5uv82dra0haedlf6kv0", "zitadel"},
		{"entra-xyz789", "entra"},
		{"google-abc123", "google"},
		{"pocketid-abc123", "pocketid"},
		{"microsoft-abc123", "microsoft"},
		{"authentik-abc123", "authentik"},
		{"keycloak-d5uv82dra0haedlf6kv0", "keycloak"},
		{"local", "local"},
		{"d6jvvp69kmnc73c9pl40", "oidc"},
		{"", "oidc"},
	}

	for _, tt := range tests {
		t.Run(tt.connectorID, func(t *testing.T) {
			result := extractIdpType(tt.connectorID)
			if result != tt.expected {
				t.Errorf("extractIdpType(%q) = %q, want %q", tt.connectorID, result, tt.expected)
			}
		})
	}
}

package metrics

import (
	"context"
	"testing"

	nbdns "github.com/netbirdio/netbird/dns"
	"github.com/netbirdio/netbird/idp/dex"
	rpservice "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
	resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
	routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
	networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
	nbpeer "github.com/netbirdio/netbird/management/server/peer"
	"github.com/netbirdio/netbird/management/server/posture"
	"github.com/netbirdio/netbird/management/server/types"
	"github.com/netbirdio/netbird/route"
)

type mockDatasource struct{}

// GetAllConnectedPeers returns a map of connected peer IDs for use in tests with predefined information
func (mockDatasource) GetAllConnectedPeers() map[string]struct{} {
	return map[string]struct{}{
		"1": {},
	}
}

// GetAllAccounts returns a list of *server.Account for use in tests with predefined information
func (mockDatasource) GetAllAccounts(_ context.Context) []*types.Account {
	localUserID := dex.EncodeDexUserID("10", "local")
	idpUserID := dex.EncodeDexUserID("20", "zitadel-d5uv82dra0haedlf6kv0")
	oidcUserID := dex.EncodeDexUserID("30", "d6jvvp69kmnc73c9pl40")
	return []*types.Account{
		{
			Id:       "1",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: false,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1", Flags: nbpeer.Flags{ServerSSHAllowed: true, RosenpassEnabled: true}},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
					SourcePostureChecks: []string{"1"},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			PostureChecks: []*posture.Checks{
				{
					ID:   "1",
					Name: "test",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.1",
						},
					},
				},
				{
					ID:   "2",
					Name: "tes2",
					Checks: posture.ChecksDefinition{
						NBVersionCheck: &posture.NBVersionCheck{
							MinVersion: "0.0.2",
						},
					},
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				localUserID: {
					Id:            localUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Services: []*rpservice.Service{
				{
					ID:      "svc1",
					Enabled: true,
					Targets: []*rpservice.Target{
						{TargetType: "peer"},
						{TargetType: "host"},
					},
					Auth: rpservice.AuthConfig{
						PasswordAuth: &rpservice.PasswordAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusActive)},
				},
				{
					ID:      "svc2",
					Enabled: false,
					Targets: []*rpservice.Target{
						{TargetType: "domain"},
					},
					Auth: rpservice.AuthConfig{
						BearerAuth: &rpservice.BearerAuthConfig{Enabled: true},
					},
					Meta: rpservice.Meta{Status: string(rpservice.StatusPending)},
				},
			},
		},
		{
			Id:       "2",
			Settings: &types.Settings{PeerLoginExpirationEnabled: true},
			SetupKeys: map[string]*types.SetupKey{
				"1": {
					Id:        "1",
					Ephemeral: true,
					UsedTimes: 1,
				},
			},
			Groups: map[string]*types.Group{
				"1": {},
				"2": {},
			},
			NameServerGroups: map[string]*nbdns.NameServerGroup{
				"1": {},
			},
			Peers: map[string]*nbpeer.Peer{
				"1": {
					ID:         "1",
					UserID:     "test",
					SSHEnabled: true,
					Meta:       nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1"},
				},
			},
			Policies: []*types.Policy{
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: true,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
				{
					Rules: []*types.PolicyRule{
						{
							Bidirectional: false,
							Protocol:      types.PolicyRuleProtocolTCP,
						},
					},
				},
			},
			Routes: map[route.ID]*route.Route{
				"1": {
					ID:         "1",
					PeerGroups: make([]string, 1),
				},
			},
			Users: map[string]*types.User{
				"1": {
					Id:            "1",
					IsServiceUser: true,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				idpUserID: {
					Id:            idpUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
				oidcUserID: {
					Id:            oidcUserID,
					IsServiceUser: false,
					PATs: map[string]*types.PersonalAccessToken{
						"1": {},
					},
				},
			},
			Networks: []*networkTypes.Network{
				{
					ID:        "1",
					AccountID: "1",
				},
			},
			NetworkResources: []*resourceTypes.NetworkResource{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
				{
					ID:        "2",
					AccountID: "1",
					NetworkID: "1",
				},
			},
			NetworkRouters: []*routerTypes.NetworkRouter{
				{
					ID:        "1",
					AccountID: "1",
					NetworkID: "1",
				},
			},
		},
	}
}

// GetStoreEngine returns FileStoreEngine
func (mockDatasource) GetStoreEngine() types.Engine {
	return types.FileStoreEngine
}

// GetCustomDomainsCounts returns test custom domain counts.
func (mockDatasource) GetCustomDomainsCounts(_ context.Context) (int64, int64, error) {
	return 3, 2, nil
}

// TestGenerateProperties tests and validate the properties generation by using the mockDatasource for the Worker.generateProperties
func TestGenerateProperties(t *testing.T) {
	ds := mockDatasource{}
	worker := Worker{
		dataSource:  ds,
		connManager: ds,
		idpManager:  EmbeddedType,
	}

	properties := worker.generateProperties(context.Background())

	if properties["accounts"] != 2 {
		t.Errorf("expected 2 accounts, got %d", properties["accounts"])
	}
	if properties["peers"] != 2 {
		t.Errorf("expected 2 peers, got %d", properties["peers"])
	}
	if properties["routes"] != 2 {
		t.Errorf("expected 2 routes, got %d", properties["routes"])
	}
	if properties["networks"] != 1 {
		t.Errorf("expected 1 networks, got %d", properties["networks"])
	}
	if properties["network_resources"] != 2 {
		t.Errorf("expected 2 network_resources, got %d", properties["network_resources"])
	}
	if properties["network_routers"] != 1 {
		t.Errorf("expected 1 network_routers, got %d", properties["network_routers"])
	}
	if properties["rules"] != 4 {
		t.Errorf("expected 4 rules, got %d", properties["rules"])
	}
	if properties["users"] != 3 {
		t.Errorf("expected 3 users, got %d", properties["users"])
	}
	if properties["setup_keys_usage"] != 2 {
		t.Errorf("expected 1 setup_keys_usage, got %d", properties["setup_keys_usage"])
	}
	if properties["pats"] != 5 {
		t.Errorf("expected 5 personal_access_tokens, got %d", properties["pats"])
	}
	if properties["peers_ssh_enabled"] != 2 {
		t.Errorf("expected 2 peers_ssh_enabled, got %d", properties["peers_ssh_enabled"])
	}
	if properties["routes_with_routing_groups"] != 2 {
		t.Errorf("expected 2 routes_with_routing_groups, got %d", properties["routes_with_routing_groups"])
	}
	if properties["rules_protocol_tcp"] != 4 {
		t.Errorf("expected 4 rules_protocol_tcp, got %d", properties["rules_protocol_tcp"])
	}
	if properties["rules_direction_oneway"] != 2 {
		t.Errorf("expected 2 rules_direction_oneway, got %d", properties["rules_direction_oneway"])
	}

	if properties["active_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_peers_last_day, got %d", properties["active_peers_last_day"])
	}
	if properties["min_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 min_active_peer_version, got %s", properties["min_active_peer_version"])
	}
	if properties["max_active_peer_version"] != "0.0.1" {
		t.Errorf("expected 0.0.1 max_active_peer_version, got %s", properties["max_active_peer_version"])
	}

	if properties["peers_login_expiration_enabled"] != 2 {
		t.Errorf("expected 2 peers_login_expiration_enabled, got %d", properties["peers_login_expiration_enabled"])
	}

	if properties["service_users"] != 2 {
		t.Errorf("expected 2 service_users, got %d", properties["service_users"])
	}

	if properties["peer_os_linux"] != 2 {
		t.Errorf("expected 2 peer_os_linux, got %d", properties["peer_os_linux"])
	}

	if properties["ephemeral_peers_setup_keys"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["ephemeral_peers_setup_keys_usage"] != 2 {
		t.Errorf("expected 2 ephemeral_peers_setup_keys_usage, got %d", properties["ephemeral_peers_setup_keys_usage"])
	}

	if properties["nameservers"] != 2 {
		t.Errorf("expected 2 nameservers, got %d", properties["nameservers"])
	}

	if properties["groups"] != 4 {
		t.Errorf("expected 4 groups, got %d", properties["groups"])
	}

	if properties["user_peers"] != 2 {
		t.Errorf("expected 2 user_peers, got %d", properties["user_peers"])
	}

	if properties["store_engine"] != types.FileStoreEngine {
		t.Errorf("expected JsonFile, got %s", properties["store_engine"])
	}

	if properties["rules_with_src_posture_checks"] != 1 {
		t.Errorf("expected 1 rules_with_src_posture_checks, got %d", properties["rules_with_src_posture_checks"])
	}

	if properties["posture_checks"] != 2 {
		t.Errorf("expected 2 posture_checks, got %d", properties["posture_checks"])
	}

	if properties["rosenpass_enabled"] != 1 {
		t.Errorf("expected 1 rosenpass_enabled, got %d", properties["rosenpass_enabled"])
	}

	if properties["active_user_peers_last_day"] != 2 {
		t.Errorf("expected 2 active_user_peers_last_day, got %d", properties["active_user_peers_last_day"])
	}

	if properties["active_users_last_day"] != 1 {
		t.Errorf("expected 1 active_users_last_day, got %d", properties["active_users_last_day"])
	}

	if properties["local_users_count"] != 1 {
		t.Errorf("expected 1 local_users_count, got %d", properties["local_users_count"])
	}
	if properties["idp_users_count"] != 2 {
		t.Errorf("expected 2 idp_users_count, got %d", properties["idp_users_count"])
	}
	if properties["embedded_idp_users_local"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_local, got %v", properties["embedded_idp_users_local"])
	}
	if properties["embedded_idp_users_zitadel"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_zitadel, got %v", properties["embedded_idp_users_zitadel"])
	}
	if properties["embedded_idp_users_oidc"] != 1 {
		t.Errorf("expected 1 embedded_idp_users_oidc, got %v", properties["embedded_idp_users_oidc"])
	}
	if properties["embedded_idp_count"] != 3 {
		t.Errorf("expected 3 embedded_idp_count, got %v", properties["embedded_idp_count"])
	}

	if properties["services"] != 2 {
		t.Errorf("expected 2 services, got %v", properties["services"])
	}
	if properties["services_enabled"] != 1 {
		t.Errorf("expected 1 services_enabled, got %v", properties["services_enabled"])
	}
	if properties["services_targets"] != 3 {
		t.Errorf("expected 3 services_targets, got %v", properties["services_targets"])
	}
	if properties["services_status_active"] != 1 {
		t.Errorf("expected 1 services_status_active, got %v", properties["services_status_active"])
	}
	if properties["services_status_pending"] != 1 {
		t.Errorf("expected 1 services_status_pending, got %v", properties["services_status_pending"])
	}
	if properties["services_status_error"] != 0 {
		t.Errorf("expected 0 services_status_error, got %v", properties["services_status_error"])
	}
	if properties["services_target_type_peer"] != 1 {
		t.Errorf("expected 1 services_target_type_peer, got %v", properties["services_target_type_peer"])
	}
	if properties["services_target_type_host"] != 1 {
		t.Errorf("expected 1 services_target_type_host, got %v", properties["services_target_type_host"])
	}
	if properties["services_target_type_domain"] != 1 {
		t.Errorf("expected 1 services_target_type_domain, got %v", properties["services_target_type_domain"])
	}
	if properties["services_auth_password"] != 1 {
		t.Errorf("expected 1 services_auth_password, got %v", properties["services_auth_password"])
	}
	if properties["services_auth_oidc"] != 1 {
		t.Errorf("expected 1 services_auth_oidc, got %v", properties["services_auth_oidc"])
	}
	if properties["services_auth_pin"] != 0 {
		t.Errorf("expected 0 services_auth_pin, got %v", properties["services_auth_pin"])
	}
	if properties["custom_domains"] != int64(3) {
		t.Errorf("expected 3 custom_domains, got %v", properties["custom_domains"])
	}
	if properties["custom_domains_validated"] != int64(2) {
		t.Errorf("expected 2 custom_domains_validated, got %v", properties["custom_domains_validated"])
	}
}

func TestExtractIdpType(t *testing.T) {
	tests := []struct {
		connectorID string
		expected    string
	}{
		{"okta-abc123def", "okta"},
		{"zitadel-d5uv82dra0haedlf6kv0", "zitadel"},
		{"entra-xyz789", "entra"},
		{"google-abc123", "google"},
		{"pocketid-abc123", "pocketid"},
		{"microsoft-abc123", "microsoft"},
		{"authentik-abc123", "authentik"},
		{"keycloak-d5uv82dra0haedlf6kv0", "keycloak"},
		{"local", "local"},
		{"d6jvvp69kmnc73c9pl40", "oidc"},
		{"", "oidc"},
	}

	for _, tt := range tests {
		t.Run(tt.connectorID, func(t *testing.T) {
			result := extractIdpType(tt.connectorID)
			if result != tt.expected {
				t.Errorf("extractIdpType(%q) = %q, want %q", tt.connectorID, result, tt.expected)
			}
		})
	}
}

-  if properties["embedded_idp_count"] != 2 {

-    t.Errorf("expected 2 embedded_idp_count, got %v", properties["embedded_idp_count"])

+  if properties["embedded_idp_users_oidc"] != 1 {

+    t.Errorf("expected 1 embedded_idp_users_oidc, got %v", properties["embedded_idp_users_oidc"])

+  if properties["embedded_idp_count"] != 3 {

+    t.Errorf("expected 3 embedded_idp_count, got %v", properties["embedded_idp_count"])


Bash

go test ./management/server/metrics/... -run 'TestExtractIdpType|TestGenerateProperties' -v 2>&1

Run metrics tests


=== RUN   TestGenerateProperties
--- PASS: TestGenerateProperties (0.00s)
=== RUN   TestExtractIdpType
=== RUN   TestExtractIdpType/okta-abc123def
=== RUN   REDACTED
=== RUN   TestExtractIdpType/entra-xyz789
=== RUN   TestExtractIdpType/google-abc123
=== RUN   TestExtractIdpType/pocketid-abc123
=== RUN   TestExtractIdpType/microsoft-abc123
=== RUN   TestExtractIdpType/authentik-abc123
=== RUN   REDACTED
=== RUN   TestExtractIdpType/local
=== RUN   REDACTED
=== RUN   TestExtractIdpType/#00
--- PASS: TestExtractIdpType (0.00s)
    --- PASS: TestExtractIdpType/okta-abc123def (0.00s)
    --- PASS: REDACTED (0.00s)
    --- PASS: TestExtractIdpType/entra-xyz789 (0.00s)
    --- PASS: TestExtractIdpType/google-abc123 (0.00s)
    --- PASS: TestExtractIdpType/pocketid-abc123 (0.00s)
    --- PASS: TestExtractIdpType/microsoft-abc123 (0.00s)
    --- PASS: TestExtractIdpType/authentik-abc123 (0.00s)
    --- PASS: REDACTED (0.00s)
    --- PASS: TestExtractIdpType/local (0.00s)
    --- PASS: REDACTED (0.00s)
    --- PASS: TestExtractIdpType/#00 (0.00s)
PASS
ok  	github.com/netbirdio/netbird/management/server/metrics	0.900s


All pass. The `TestGenerateProperties` now covers all three connector types with real dex-encoded user IDs:

- `local` → `embedded_idp_users_local = 1`
- `zitadel-d5uv82dra0haedlf6kv0` (prefixed) → `embedded_idp_users_zitadel = 1`
- `d6jvvp69kmnc73c9pl40` (generic OIDC, no prefix) → `embedded_idp_users_oidc = 1`
- `embedded_idp_count = 3` (local + zitadel + oidc)


create a pr description and a title


**Title:** Fix embedded IdP metrics to count local and generic OIDC users

**Description:**

## Summary
- Fix `extractIdpType` to return `"local"` for local connector IDs instead of `"oidc"`, and `"oidc"` for generic OIDC connectors (bare xid, no type prefix)
- Include local users in `embeddedIdpTypes` map so `embedded_idp_count` and `embedded_idp_users_local` metrics are emitted
- Add generic OIDC user (no-prefix connector) to test data to cover all three connector types

## What was broken
- `embedded_idp_count` excluded local as a type, understating the number of configured IdP types
- No `embedded_idp_users_local` metric was emitted despite local users being tracked separately
- `extractIdpType("local")` would have returned `"oidc"` (wrong) if ever called directly

## Test plan
- [x] `TestExtractIdpType` covers local, generic OIDC (bare xid), prefixed connectors, and empty string
- [x] `TestGenerateProperties` validates all three user types with real dex-encoded IDs: local, zitadel (prefixed), and generic OIDC (no prefix)