package model_test import ( "encoding/json" "os" "github.com/google/uuid" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/bakito/adguardhome-sync/internal/client/model" "github.com/bakito/adguardhome-sync/internal/types" "github.com/bakito/adguardhome-sync/internal/utils" ) var _ = Describe("Types", func() { var ( url string apiPath string ) BeforeEach(func() { url = "https://" + uuid.NewString() apiPath = "/" + uuid.NewString() }) Context("FilteringStatus", func() { It("should correctly parse json", func() { b, err := os.ReadFile("../../../testdata/filtering-status.json") fs := &model.FilterStatus{} Ω(err).ShouldNot(HaveOccurred()) err = json.Unmarshal(b, fs) Ω(err).ShouldNot(HaveOccurred()) }) }) Context("Filters", func() { Context("Merge", func() { var ( originFilters []model.Filter replicaFilters []model.Filter ) BeforeEach(func() { originFilters = []model.Filter{} replicaFilters = []model.Filter{} }) It("should add a missing filter", func() { originFilters = append(originFilters, model.Filter{Url: url}) a, u, d := model.MergeFilters(&replicaFilters, &originFilters) Ω(a).Should(HaveLen(1)) Ω(u).Should(BeEmpty()) Ω(d).Should(BeEmpty()) Ω(a[0].Url).Should(Equal(url)) }) It("should remove additional filter", func() { replicaFilters = append(replicaFilters, model.Filter{Url: url}) a, u, d := model.MergeFilters(&replicaFilters, &originFilters) Ω(a).Should(BeEmpty()) Ω(u).Should(BeEmpty()) Ω(d).Should(HaveLen(1)) Ω(d[0].Url).Should(Equal(url)) }) It("should update existing filter when enabled differs", func() { enabled := true originFilters = append(originFilters, model.Filter{Url: url, Enabled: enabled}) replicaFilters = append(replicaFilters, model.Filter{Url: url, Enabled: !enabled}) a, u, d := model.MergeFilters(&replicaFilters, &originFilters) Ω(a).Should(BeEmpty()) Ω(u).Should(HaveLen(1)) Ω(d).Should(BeEmpty()) Ω(u[0].Enabled).Should(Equal(enabled)) }) It("should update existing filter when name differs", func() { name1 := uuid.NewString() name2 := uuid.NewString() originFilters = append(originFilters, model.Filter{Url: url, Name: name1}) replicaFilters = append(replicaFilters, model.Filter{Url: url, Name: name2}) a, u, d := model.MergeFilters(&replicaFilters, &originFilters) Ω(a).Should(BeEmpty()) Ω(u).Should(HaveLen(1)) Ω(d).Should(BeEmpty()) Ω(u[0].Name).Should(Equal(name1)) }) It("should have no changes", func() { originFilters = append(originFilters, model.Filter{Url: url}) replicaFilters = append(replicaFilters, model.Filter{Url: url}) a, u, d := model.MergeFilters(&replicaFilters, &originFilters) Ω(a).Should(BeEmpty()) Ω(u).Should(BeEmpty()) Ω(d).Should(BeEmpty()) }) }) }) Context("AdGuardInstance", func() { It("should build a key with url and api apiPath", func() { i := &types.AdGuardInstance{URL: url, APIPath: apiPath} Ω(i.Key()).Should(Equal(url + "#" + apiPath)) }) }) Context("RewriteEntry", func() { It("should build a key with url and api apiPath", func() { domain := uuid.NewString() answer := uuid.NewString() re := &model.RewriteEntry{Domain: utils.Ptr(domain), Answer: utils.Ptr(answer)} Ω(re.Key()).Should(Equal(domain + "#" + answer)) }) }) Context("QueryLogConfig", func() { Context("Equal", func() { var ( a *model.QueryLogConfigWithIgnored b *model.QueryLogConfigWithIgnored ) BeforeEach(func() { a = &model.QueryLogConfigWithIgnored{} b = &model.QueryLogConfigWithIgnored{} }) It("should be equal", func() { a.Enabled = utils.Ptr(true) var interval model.QueryLogConfigInterval = 1 a.Interval = &interval a.AnonymizeClientIp = utils.Ptr(true) b.Enabled = utils.Ptr(true) b.Interval = &interval b.AnonymizeClientIp = utils.Ptr(true) Ω(a.Equals(b)).Should(BeTrue()) }) It("should not be equal when enabled differs", func() { a.Enabled = utils.Ptr(true) b.Enabled = utils.Ptr(false) Ω(a.Equals(b)).ShouldNot(BeTrue()) }) It("should not be equal when interval differs", func() { var interval1 model.QueryLogConfigInterval = 1 var interval2 model.QueryLogConfigInterval = 2 a.Interval = &interval1 b.Interval = &interval2 Ω(a.Equals(b)).ShouldNot(BeTrue()) }) It("should not be equal when anonymizeClientIP differs", func() { a.AnonymizeClientIp = utils.Ptr(true) b.AnonymizeClientIp = utils.Ptr(false) Ω(a.Equals(b)).ShouldNot(BeTrue()) }) }) }) Context("RewriteEntries", func() { Context("Merge", func() { var ( originRE model.RewriteEntries replicaRE model.RewriteEntries domain string ) BeforeEach(func() { originRE = model.RewriteEntries{} replicaRE = model.RewriteEntries{} domain = uuid.NewString() }) It("should add a missing rewrite entry", func() { originRE = append(originRE, model.RewriteEntry{Domain: utils.Ptr(domain)}) a, r, d := replicaRE.Merge(&originRE) Ω(a).Should(HaveLen(1)) Ω(r).Should(BeEmpty()) Ω(d).Should(BeEmpty()) Ω(*a[0].Domain).Should(Equal(domain)) }) It("should remove additional rewrite entry", func() { replicaRE = append(replicaRE, model.RewriteEntry{Domain: utils.Ptr(domain)}) a, r, d := replicaRE.Merge(&originRE) Ω(a).Should(BeEmpty()) Ω(r).Should(HaveLen(1)) Ω(d).Should(BeEmpty()) Ω(*r[0].Domain).Should(Equal(domain)) }) It("should have no changes", func() { originRE = append(originRE, model.RewriteEntry{Domain: utils.Ptr(domain)}) replicaRE = append(replicaRE, model.RewriteEntry{Domain: utils.Ptr(domain)}) a, r, d := replicaRE.Merge(&originRE) Ω(a).Should(BeEmpty()) Ω(r).Should(BeEmpty()) Ω(d).Should(BeEmpty()) }) It("should remove target duplicate", func() { originRE = append(originRE, model.RewriteEntry{Domain: utils.Ptr(domain)}) replicaRE = append(replicaRE, model.RewriteEntry{Domain: utils.Ptr(domain)}) replicaRE = append(replicaRE, model.RewriteEntry{Domain: utils.Ptr(domain)}) a, r, d := replicaRE.Merge(&originRE) Ω(a).Should(BeEmpty()) Ω(r).Should(HaveLen(1)) Ω(d).Should(BeEmpty()) }) It("should remove target duplicate", func() { originRE = append(originRE, model.RewriteEntry{Domain: utils.Ptr(domain)}) originRE = append(originRE, model.RewriteEntry{Domain: utils.Ptr(domain)}) replicaRE = append(replicaRE, model.RewriteEntry{Domain: utils.Ptr(domain)}) a, r, d := replicaRE.Merge(&originRE) Ω(a).Should(BeEmpty()) Ω(r).Should(BeEmpty()) Ω(d).Should(HaveLen(1)) }) }) }) Context("Config", func() { var cfg *types.Config BeforeEach(func() { cfg = &types.Config{} }) Context("UniqueReplicas", func() { It("should be empty if noting defined", func() { r := cfg.UniqueReplicas() Ω(r).Should(BeEmpty()) }) It("should be empty if replica url is not set", func() { cfg.Replica = &types.AdGuardInstance{URL: ""} r := cfg.UniqueReplicas() Ω(r).Should(BeEmpty()) }) It("should be empty if replicas url is not set", func() { cfg.Replicas = []types.AdGuardInstance{{URL: ""}} r := cfg.UniqueReplicas() Ω(r).Should(BeEmpty()) }) It("should return only one replica if same url and apiPath", func() { cfg.Replica = &types.AdGuardInstance{URL: url, APIPath: apiPath} cfg.Replicas = []types.AdGuardInstance{{URL: url, APIPath: apiPath}, {URL: url, APIPath: apiPath}} r := cfg.UniqueReplicas() Ω(r).Should(HaveLen(1)) }) It("should return 3 one replicas if urls are different", func() { cfg.Replica = &types.AdGuardInstance{URL: url, APIPath: apiPath} cfg.Replicas = []types.AdGuardInstance{ {URL: url + "1", APIPath: apiPath}, {URL: url, APIPath: apiPath + "1"}, } r := cfg.UniqueReplicas() Ω(r).Should(HaveLen(3)) }) It("should set default api apiPath if not set", func() { cfg.Replica = &types.AdGuardInstance{URL: url} cfg.Replicas = []types.AdGuardInstance{{URL: url + "1"}} r := cfg.UniqueReplicas() Ω(r).Should(HaveLen(2)) Ω(r[0].APIPath).Should(Equal(types.DefaultAPIPath)) Ω(r[1].APIPath).Should(Equal(types.DefaultAPIPath)) }) }) }) Context("Clients", func() { Context("Merge", func() { var ( originClients *model.Clients replicaClients model.Clients name string ) BeforeEach(func() { originClients = &model.Clients{} replicaClients = model.Clients{} name = uuid.NewString() }) It("should add a missing client", func() { originClients.Add(model.Client{Name: utils.Ptr(name)}) a, u, d := replicaClients.Merge(originClients) Ω(a).Should(HaveLen(1)) Ω(u).Should(BeEmpty()) Ω(d).Should(BeEmpty()) Ω(*a[0].Name).Should(Equal(name)) }) It("should remove additional client", func() { replicaClients.Add(model.Client{Name: utils.Ptr(name)}) a, u, d := replicaClients.Merge(originClients) Ω(a).Should(BeEmpty()) Ω(u).Should(BeEmpty()) Ω(d).Should(HaveLen(1)) Ω(*d[0].Name).Should(Equal(name)) }) It("should update existing client when name differs", func() { disallowed := true originClients.Add(model.Client{Name: utils.Ptr(name), FilteringEnabled: utils.Ptr(disallowed)}) replicaClients.Add(model.Client{Name: utils.Ptr(name), FilteringEnabled: utils.Ptr(!disallowed)}) a, u, d := replicaClients.Merge(originClients) Ω(a).Should(BeEmpty()) Ω(u).Should(HaveLen(1)) Ω(d).Should(BeEmpty()) Ω(*u[0].FilteringEnabled).Should(Equal(disallowed)) }) }) }) Context("Client", func() { Context("Equals", func() { var ( cl1 *model.Client cl2 *model.Client ) BeforeEach(func() { cl1 = &model.Client{ Name: utils.Ptr("foo"), BlockedServicesSchedule: &model.Schedule{TimeZone: utils.Ptr("UTC")}, } cl2 = &model.Client{ Name: utils.Ptr("foo"), BlockedServicesSchedule: &model.Schedule{TimeZone: utils.Ptr("Local")}, } }) It("should equal if only timezone differs on empty blocked service schedule", func() { Ω(cl1.Equals(cl2)).Should(BeTrue()) }) }) }) Context("BlockedServices", func() { Context("Equals", func() { It("should be equal", func() { s1 := &model.BlockedServicesArray{"a", "b"} s2 := &model.BlockedServicesArray{"b", "a"} Ω(model.EqualsStringSlice(s1, s2, true)).Should(BeTrue()) }) It("should not be equal different values", func() { s1 := &model.BlockedServicesArray{"a", "b"} s2 := &model.BlockedServicesArray{"B", "a"} Ω(model.EqualsStringSlice(s1, s2, true)).ShouldNot(BeTrue()) }) It("should not be equal different length", func() { s1 := &model.BlockedServicesArray{"a", "b"} s2 := &model.BlockedServicesArray{"b", "a", "c"} Ω(model.EqualsStringSlice(s1, s2, true)).ShouldNot(BeTrue()) }) }) }) Context("DNSConfig", func() { Context("Equals", func() { It("should be equal", func() { dc1 := &model.DNSConfig{LocalPtrUpstreams: utils.Ptr([]string{"a"})} dc2 := &model.DNSConfig{LocalPtrUpstreams: utils.Ptr([]string{"a"})} Ω(dc1.Equals(dc2)).Should(BeTrue()) }) It("should not be equal", func() { dc1 := &model.DNSConfig{LocalPtrUpstreams: utils.Ptr([]string{"a"})} dc2 := &model.DNSConfig{LocalPtrUpstreams: utils.Ptr([]string{"b"})} Ω(dc1.Equals(dc2)).ShouldNot(BeTrue()) }) }) }) Context("DHCPServerConfig", func() { Context("Equals", func() { It("should be equal", func() { dc1 := &model.DhcpStatus{ V4: &model.DhcpConfigV4{ GatewayIp: utils.Ptr("1.2.3.4"), LeaseDuration: utils.Ptr(123), RangeStart: utils.Ptr("1.2.3.5"), RangeEnd: utils.Ptr("1.2.3.6"), SubnetMask: utils.Ptr("255.255.255.0"), }, } dc2 := &model.DhcpStatus{ V4: &model.DhcpConfigV4{ GatewayIp: utils.Ptr("1.2.3.4"), LeaseDuration: utils.Ptr(123), RangeStart: utils.Ptr("1.2.3.5"), RangeEnd: utils.Ptr("1.2.3.6"), SubnetMask: utils.Ptr("255.255.255.0"), }, } Ω(dc1.Equals(dc2)).Should(BeTrue()) }) It("should not be equal", func() { dc1 := &model.DhcpStatus{ V4: &model.DhcpConfigV4{ GatewayIp: utils.Ptr("1.2.3.3"), LeaseDuration: utils.Ptr(123), RangeStart: utils.Ptr("1.2.3.5"), RangeEnd: utils.Ptr("1.2.3.6"), SubnetMask: utils.Ptr("255.255.255.0"), }, } dc2 := &model.DhcpStatus{ V4: &model.DhcpConfigV4{ GatewayIp: utils.Ptr("1.2.3.4"), LeaseDuration: utils.Ptr(123), RangeStart: utils.Ptr("1.2.3.5"), RangeEnd: utils.Ptr("1.2.3.6"), SubnetMask: utils.Ptr("255.255.255.0"), }, } Ω(dc1.Equals(dc2)).ShouldNot(BeTrue()) }) }) Context("Clone", func() { It("clone should be equal", func() { dc1 := &model.DhcpStatus{ V4: &model.DhcpConfigV4{ GatewayIp: utils.Ptr("1.2.3.4"), LeaseDuration: utils.Ptr(123), RangeStart: utils.Ptr("1.2.3.5"), RangeEnd: utils.Ptr("1.2.3.6"), SubnetMask: utils.Ptr("255.255.255.0"), }, } Ω(dc1.Clone().Equals(dc1)).Should(BeTrue()) }) }) Context("HasConfig", func() { It("should not have a config", func() { dc1 := &model.DhcpStatus{ V4: &model.DhcpConfigV4{}, V6: &model.DhcpConfigV6{}, } Ω(dc1.HasConfig()).Should(BeFalse()) }) It("should not have a v4 config with nil IP", func() { dc1 := &model.DhcpStatus{ V4: &model.DhcpConfigV4{ GatewayIp: nil, }, V6: &model.DhcpConfigV6{ RangeStart: utils.Ptr("1.2.3.5"), }, } Ω(dc1.HasConfig()).Should(BeTrue()) }) It("should not have a v4 config with empty IP", func() { dc1 := &model.DhcpStatus{ V4: &model.DhcpConfigV4{ GatewayIp: utils.Ptr(""), }, V6: &model.DhcpConfigV6{ RangeStart: utils.Ptr("1.2.3.5"), }, } Ω(dc1.HasConfig()).Should(BeTrue()) }) It("should not have a v6 config", func() { dc1 := &model.DhcpStatus{ V4: &model.DhcpConfigV4{ GatewayIp: utils.Ptr("1.2.3.4"), LeaseDuration: utils.Ptr(123), RangeStart: utils.Ptr("1.2.3.5"), RangeEnd: utils.Ptr("1.2.3.6"), SubnetMask: utils.Ptr("255.255.255.0"), }, V6: &model.DhcpConfigV6{}, } Ω(dc1.HasConfig()).Should(BeTrue()) }) }) }) })