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)


make it sound like ad for X ads: New in NetBird v0.66: netbird expose
One command. Secure public URL. Auto-cleanup when you stop it. Share a dev server, demo a feature, test a webhook. Add --with-pin, --with-password, or --with-user-groups to lock it down.
https://t.co/hMzqu0cCpa


**New in NetBird v0.66: `netbird expose`**

One command. Secure public URL. Auto-cleanup when you stop it.

Share a dev server, demo a feature, test a webhook — no tunnels, no config files, no port forwarding.

Add `--with-pin`, `--with-password`, or `--with-user-groups` to lock it down.

https://t.co/hMzqu0cCpa


repharse this: The fastest way to share your local project with your friends/colleagues/clients.

No Vercel, no GitHub.


The fastest way to share what you're building — no deploy, no push, no waiting.


more options


1. Share what's on your localhost with anyone. No deploy. No push. No waiting.

2. From your machine to a public URL in one command. Skip the deploy pipeline.

3. Your localhost, instantly public. No Vercel. No GitHub. No deploy step.

4. Stop deploying just to show someone what you're working on.

5. One command to go from "works on my machine" to "here's the link."

6. Share your local build with anyone, anywhere. No CI/CD required.


how t ocheck for harddrive exncryption with osquery?


WebSearch

Question: osquery check hard drive disk encryption query 2025


Web search results for query: "osquery check hard drive disk encryption query 2025"

Links: [{"title":"Osquery Tutorial: How to Check Disk Encryption on Mac, Linux & Windows","url":"https://www.uptycs.com/blog/osquery-tutorial-how-to-check-disk-encryption-on-mac-linux-and-windows"},{"title":"Monitoring hard drives (HDD) with Osquery | by Zercurity | Medium","url":"https://zercurity.medium.com/monitoring-hard-drives-hdd-with-osquery-a796e647fdf8"},{"title":"Fleet | \"disk_encryption\" in osquery | Fleet documentation","url":"https://fleetdm.com/tables/disk_encryption"},{"title":"SSD Encryption Vulnerabilities: Osquery Identifies Windows Machine Vuln","url":"https://www.uptycs.com/blog/vulnerabilities-in-ssd-encryption-using-osquery-to-identify-affected-machines"},{"title":"Fleet | \"bitlocker_info\" in osquery | Fleet documentation","url":"https://fleetdm.com/tables/bitlocker_info"},{"title":"Managing compliance with Osquery for local & remote workers | by Zercurity | Medium","url":"https://zercurity.medium.com/managing-compliance-with-osquery-for-local-remote-workers-4fbf7c3dc406"},{"title":"osquery `disk_encryption` table incorrectly states disk is not encrypted on linux · Issue #7889 · osquery/osquery","url":"https://github.com/osquery/osquery/issues/7889"},{"title":"osquery/osquery/tables/system/linux/disk_encryption.cpp at master · osquery/osquery","url":"https://github.com/osquery/osquery/blob/master/osquery/tables/system/linux/disk_encryption.cpp"},{"title":"< Julian Scala> you will want to use the `disk encryption` t osquery #macos","url":"https://chat.osquery.REDACTED"},{"title":"osquery/specs/posix/disk_encryption.table at master · osquery/osquery","url":"https://github.com/osquery/osquery/blob/master/specs/posix/disk_encryption.table"}]

I found comprehensive information about checking disk encryption with osquery. Here's what the search results reveal:

## Key Tables for Disk Encryption Checking

Osquery provides two tables: bitlocker_info for Windows and disk_encryption for Mac and Linux.

## Windows Queries

If protection_status > 0, then the mounted drive is encrypted. More specifically, you need to ensure both the conversion_status and protection_status are both 1 if you want to ensure the disk is encrypted.

A common query for Windows is: SELECT * FROM bitlocker_info WHERE drive_letter='C:' AND protection_status != 1; to list systems that do not have BitLocker enabled on their OS drive.

## Mac/Linux Queries

You can join information from the disk_encryption table to the output of the mounts table. For example, SELECT COUNT(*) AS passed FROM disk_encryption WHERE name IN ( SELECT device FROM mounts WHERE path = '/' );

Generally, look for root partitions ("/") and other paths mounted off root, like /home, /var, or /System/Volumes/Data. Those should be encrypted.

## Important Notes

The disk_encryption table only captures information on Linux and macOS-based systems. There is no common hard drive table within Osquery; each operating system platform has its own distinct tables.

REMINDER: You MUST include the sources above in your response to the user using markdown hyperlinks.

Question: osquery check hard drive disk encryption query 2025

Fleet | "disk_encryption" in osquery | Fleet documentation

Fleet | "bitlocker_info" in osquery | Fleet documentation


### Cross-platform

```sql
SELECT * FROM disk_encryption;
```
Works on **macOS** (FileVault) and **Linux** (LUKS/dm-crypt). Check the `encrypted` column.

### macOS / Linux — root volume encrypted?

```sql
SELECT d.name, d.encrypted
FROM disk_encryption d
JOIN mounts m ON d.name = m.device
WHERE m.path = '/';
```

### Windows — BitLocker

```sql
SELECT drive_letter, protection_status, conversion_status, encryption_method
FROM bitlocker_info;
```

- `protection_status = 1` → encryption active
- `conversion_status = 1` → fully encrypted
- Check `drive_letter = 'C:'` for the OS drive

### Quick compliance check

```sql
-- macOS/Linux: returns 0 if root is NOT encrypted
SELECT COUNT(*) AS encrypted FROM disk_encryption
WHERE name IN (SELECT device FROM mounts WHERE path = '/');

-- Windows: returns rows where BitLocker is NOT enabled on C:
SELECT * FROM bitlocker_info
WHERE drive_letter = 'C:' AND protection_status != 1;
```

Sources:
- [Osquery Tutorial: Disk Encryption on Mac, Linux & Windows](https://www.uptycs.com/blog/osquery-tutorial-how-to-check-disk-encryption-on-mac-linux-and-windows)
- [Fleet: disk_encryption table docs](https://fleetdm.com/tables/disk_encryption)
- [Fleet: bitlocker_info table docs](https://fleetdm.com/tables/bitlocker_info)


how does crowdstrike use osquery


Question: CrowdStrike osquery integration how does it use osquery 2025


Web search results for query: "CrowdStrike osquery integration how does it use osquery 2025"

Links: [{"title":"What is osquery? Core Functions and Best Practices | CrowdStrike","url":"https://www.crowdstrike.com/en-us/cybersecurity-101/it-automation/osquery/"},{"title":"GitHub - bdemetris/osquery-crowdstrike-ext: osquery extension to get basic info from crowdstrike falcon via falconctl","url":"https://github.com/bdemetris/osquery-crowdstrike-ext"},{"title":"osquery vs CrowdStrike | What are the differences? | StackShare","url":"https://stackshare.io/stackups/crowdstrike-vs-osquery"},{"title":"CrowdStrike Products Falcon for IT","url":"https://www.crowdstrike.com/wp-content/uploads/2024/02/crowdstrike-falcon-for-IT-data-sheet.pdf"},{"title":"How to use OSQuery for Windows Events","url":"https://www.linkedin.REDACTED"},{"title":"Falcon for IT: Unified, AI-powered IT Automation | CrowdStrike","url":"https://www.crowdstrike.com/en-us/platform/falcon-for-it/"},{"title":"CrowdStrike Products Falcon Foundry Cybersecurity’s first low-code application","url":"https://www.crowdstrike.com/wp-content/uploads/2024/05/crowdstrike-falcon-foundry-data-sheet.pdf"},{"title":"How to tell if CrowdStrike Falcon sensor is running | 1Password","url":"https://1password.com/blog/how-to-tell-if-crowdstrike-falcon-sensor-is-running"},{"title":"osquery: Details, Reviews, Pricing, & Features | CheckThat.ai","url":"https://checkthat.ai/brands/osquery"},{"title":"\"crowdstrike_falcon\" in osquery | Fleet documentation","url":"https://fleetdm.com/tables/crowdstrike_falcon"}]

Based on the search results, here's how CrowdStrike integrates and uses osquery:

## Primary Integration Points

CrowdStrike's Falcon for IT uses Charlotte AI to turn plain language into precise osquery queries — delivering real-time visibility across your entire endpoint fleet. The platform allows querying any asset running Windows, Linux and Mac by leveraging the flexible OSQuery language.

## Falcon for IT Implementation

Organizations can ask any question on demand using generative AI prompts, a robust query language and 150+ out-of-the-box queries for common questions. This makes osquery more accessible to users who may not be familiar with SQL syntax.

## Use Cases

CrowdStrike uses osquery for querying configurations, monitoring processes and network connections, detecting anomalies, and tracking user activities.

## Data Integration

Osquery integrates with your organization's existing security tools and platforms, providing a simplified export of data extracted with queries. This allows you to leverage queried data further by processing it through other security monitoring and incident management tools.

## Development Platforms

Falcon Foundry provides users with Falcon RTR and OSquery, giving them the ability to fetch, transform and store any data. Users can also access CrowdStrike Falcon data from their existing modules or data stored in the Falcon platform to build their apps.

REMINDER: You MUST include the sources above in your response to the user using markdown hyperlinks.

Question: CrowdStrike osquery integration how does it use osquery 2025

CrowdStrike Products Falcon for IT

How to use OSQuery for Windows Events

"crowdstrike_falcon" in osquery | Fleet documentation


CrowdStrike doesn't use osquery as its core detection engine — the Falcon sensor is proprietary. Instead, they've **embedded osquery as a querying layer** on top of their platform:

### Falcon for IT
Their main osquery integration. The Falcon agent ships with a built-in osquery runtime, so you don't need to deploy osquery separately. You get:
- **Real-time queries** across your entire fleet (Windows, Linux, macOS) using osquery SQL
- **Charlotte AI** translates natural language into osquery queries (e.g., "which machines have unencrypted disks?" → generates the SQL)
- **150+ pre-built queries** for common IT/security questions
- Scheduled queries for continuous monitoring

### Falcon Foundry (low-code platform)
Developers can use **Falcon RTR + osquery** to fetch, transform, and store arbitrary endpoint data. This powers custom apps built on top of the Falcon platform.

### What they use it for
- Endpoint inventory and compliance (installed software, disk encryption, OS versions)
- Incident response (query live state across thousands of hosts)
- Configuration drift detection
- Vulnerability assessment (check patch levels, running services)

### What osquery does NOT do in CrowdStrike
- It's **not** the detection/prevention engine — that's the Falcon sensor's proprietary kernel-level agent
- It's **not** used for real-time threat blocking
- It's a **visibility/query tool**, not a security enforcement tool

Essentially, CrowdStrike uses osquery the same way Fleet or Kolide do — as a SQL interface to ask questions about endpoint state — but bundled into their existing agent so there's no separate deployment.

Sources:
- [What is osquery? | CrowdStrike](https://www.crowdstrike.com/en-us/cybersecurity-101/it-automation/osquery/)
- [Falcon for IT: Unified, AI-powered IT Automation](https://www.crowdstrike.com/en-us/platform/falcon-for-it/)
- [CrowdStrike Falcon for IT Data Sheet (PDF)](https://www.crowdstrike.com/wp-content/uploads/2024/02/crowdstrike-falcon-for-IT-data-sheet.pdf)
- [crowdstrike_falcon osquery table | Fleet](https://fleetdm.com/tables/crowdstrike_falcon)


what are 150 predefined queries


Question: CrowdStrike Falcon for IT 150 predefined osquery queries list examples